Skip to content

Commit 2da6961

Browse files
committed
FitFileDataProcessor cache methods not just method names
1 parent 9d8740d commit 2da6961

File tree

2 files changed

+50
-33
lines changed

2 files changed

+50
-33
lines changed

fitparse/base.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
except NameError:
1010
num_types = (int, float)
1111

12-
from fitparse.processors import FitFileDataProcessor
12+
from fitparse import processors
1313
from fitparse.profile import FIELD_TYPE_TIMESTAMP, MESSAGE_TYPES
1414
from fitparse.records import (
1515
DataMessage, FieldData, FieldDefinition, DevFieldDefinition, DefinitionMessage, MessageHeader,
@@ -35,7 +35,7 @@ def __init__(self, fileish, check_crc=True, data_processor=None):
3535
self._file = io.BytesIO(fileish)
3636

3737
self.check_crc = check_crc
38-
self._processor = data_processor or FitFileDataProcessor()
38+
self._processor = data_processor or processors.get_default_processor()
3939

4040
# Get total filesize
4141
self._file.seek(0, os.SEEK_END)

fitparse/processors.py

+48-31
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,36 @@
11
import datetime
2+
23
from fitparse.utils import scrub_method_name
34

45
# Datetimes (uint32) represent seconds since this UTC_REFERENCE
56
UTC_REFERENCE = 631065600 # timestamp for UTC 00:00 Dec 31 1989
67

78

89
class FitFileDataProcessor(object):
10+
"""
11+
Processor to change raw values to more comfortable ones.
12+
Uses method cache to speed up the processing - reuse the object if used multiple times.
13+
"""
14+
915
# TODO: Document API
1016
# Functions that will be called to do the processing:
11-
#def run_type_processor(field_data)
12-
#def run_field_processor(field_data)
13-
#def run_unit_processor(field_data)
14-
#def run_message_processor(data_message)
17+
# def run_type_processor(field_data)
18+
# def run_field_processor(field_data)
19+
# def run_unit_processor(field_data)
20+
# def run_message_processor(data_message)
1521

1622
# By default, the above functions call these functions if they exist:
17-
#def process_type_<type_name> (field_data)
18-
#def process_field_<field_name> (field_data) -- can be unknown_DD but NOT recommended
19-
#def process_units_<unit_name> (field_data)
20-
#def process_message_<mesg_name / mesg_type_num> (data_message)
23+
# def process_type_<type_name> (field_data)
24+
# def process_field_<field_name> (field_data) -- can be unknown_DD but NOT recommended
25+
# def process_units_<unit_name> (field_data)
26+
# def process_message_<mesg_name / mesg_type_num> (data_message)
2127

22-
# Used to memoize scrubbed method names
23-
_scrubbed_method_names = {}
28+
def __init__(self):
29+
# Used to memoize scrubbed methods
30+
self._method_cache = {}
2431

25-
def _scrub_method_name(self, method_name):
26-
"""Scrubs a method name, returning result from local cache if available.
32+
def _get_scrubbed_method(self, method_name):
33+
"""Scrubs a method name and cache it _method_cache.
2734
2835
This method wraps fitparse.utils.scrub_method_name and memoizes results,
2936
as scrubbing a method name is expensive.
@@ -32,36 +39,38 @@ def _scrub_method_name(self, method_name):
3239
method_name: Method name to scrub.
3340
3441
Returns:
35-
Scrubbed method name.
42+
Scrubbed method (bounded).
3643
"""
37-
if method_name not in self._scrubbed_method_names:
38-
self._scrubbed_method_names[method_name] = (
39-
scrub_method_name(method_name))
44+
method = self._method_cache.get(method_name, False)
45+
if method is not False:
46+
return method
47+
48+
scrubbed_method_name = scrub_method_name(method_name)
49+
try:
50+
method = getattr(self, scrubbed_method_name)
51+
except AttributeError:
52+
method = None
53+
self._method_cache[method_name] = method
54+
return method
4055

41-
return self._scrubbed_method_names[method_name]
56+
def _run_processor(self, method_name, data):
57+
method = self._get_scrubbed_method(method_name)
58+
if method is None:
59+
return
60+
method(data)
4261

4362
def run_type_processor(self, field_data):
44-
self._run_processor(self._scrub_method_name(
45-
'process_type_%s' % field_data.type.name), field_data)
63+
self._run_processor('process_type_%s' % field_data.type.name, field_data)
4664

4765
def run_field_processor(self, field_data):
48-
self._run_processor(self._scrub_method_name(
49-
'process_field_%s' % field_data.name), field_data)
66+
self._run_processor('process_field_%s' % field_data.name, field_data)
5067

5168
def run_unit_processor(self, field_data):
5269
if field_data.units:
53-
self._run_processor(self._scrub_method_name(
54-
'process_units_%s' % field_data.units), field_data)
70+
self._run_processor('process_units_%s' % field_data.units, field_data)
5571

5672
def run_message_processor(self, data_message):
57-
self._run_processor(self._scrub_method_name(
58-
'process_message_%s' % data_message.def_mesg.name), data_message)
59-
60-
def _run_processor(self, processor_name, data):
61-
try:
62-
getattr(self, processor_name)(data)
63-
except AttributeError:
64-
pass
73+
self._run_processor('process_message_%s' % data_message.def_mesg.name, data_message)
6574

6675
def process_type_bool(self, field_data):
6776
if field_data.value is not None:
@@ -114,3 +123,11 @@ def process_units_semicircles(self, field_data):
114123
if field_data.value is not None:
115124
field_data.value *= 180.0 / (2 ** 31)
116125
field_data.units = 'deg'
126+
127+
128+
_DEFAULT_PROCESSOR = FitFileDataProcessor()
129+
130+
131+
def get_default_processor():
132+
"""Default, shared instance of processor. (Due to the method cache.)"""
133+
return _DEFAULT_PROCESSOR

0 commit comments

Comments
 (0)