Skip to content

Commit d511f0a

Browse files
committed
Add Python 2.7 compatibility
Because apparently we're *still* not ready to move on...
1 parent 3d8362b commit d511f0a

5 files changed

Lines changed: 51 additions & 17 deletions

File tree

‎fitparse/processors.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import contextlib
21
import datetime
32
from fitparse.utils import scrub_method_name
43

@@ -34,8 +33,10 @@ def run_message_processor(self, data_message):
3433
self._run_processor(scrub_method_name('process_message_%s' % data_message.def_mesg.name), data_message)
3534

3635
def _run_processor(self, processor_name, data):
37-
with contextlib.suppress(AttributeError):
36+
try:
3837
getattr(self, processor_name)(data)
38+
except AttributeError:
39+
pass
3940

4041
def process_type_bool(self, field_data):
4142
if field_data.value is not None:
@@ -72,7 +73,7 @@ def run_field_processor(self, field_data):
7273
if field_data.name.endswith("_speed"):
7374
self.process_field_speed(field_data)
7475
else:
75-
super().run_field_processor(field_data)
76+
super(StandardUnitsDataProcessor, self).run_field_processor(field_data)
7677

7778
def process_field_distance(self, field_data):
7879
if field_data.value is not None:

‎fitparse/records.py‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import math
22
import struct
3-
import itertools
3+
4+
# Python 2 compat
5+
try:
6+
from itertools import zip_longest
7+
except ImportError:
8+
from itertools import izip_longest as zip_longest
49

510
from fitparse.utils import FitParseError
611

@@ -16,7 +21,7 @@ class RecordBase(object):
1621
# and see if that gives us any performance improvements
1722

1823
def __init__(self, *args, **kwargs):
19-
for slot_name, value in itertools.zip_longest(self.__slots__, args, fillvalue=None):
24+
for slot_name, value in zip_longest(self.__slots__, args, fillvalue=None):
2025
setattr(self, slot_name, value)
2126
for slot_name, value in kwargs.items():
2227
setattr(self, slot_name, value)
@@ -321,6 +326,14 @@ def render(self, raw_value):
321326
return raw_value
322327

323328

329+
def parse_string(string):
330+
try:
331+
end = string.index(0x00)
332+
except TypeError: # Python 2 compat
333+
end = string.index('\x00')
334+
335+
return string[:end].decode('utf-8', errors='replace') or None
336+
324337
# The default base type
325338
BASE_TYPE_BYTE = BaseType(name='byte', identifier=0x0D, fmt='B', parse=lambda x: None if all(b == 0xFF for b in x) else x)
326339

@@ -332,7 +345,7 @@ def render(self, raw_value):
332345
0x84: BaseType(name='uint16', identifier=0x84, fmt='H', parse=lambda x: None if x == 0xFFFF else x),
333346
0x85: BaseType(name='sint32', identifier=0x85, fmt='i', parse=lambda x: None if x == 0x7FFFFFFF else x),
334347
0x86: BaseType(name='uint32', identifier=0x86, fmt='I', parse=lambda x: None if x == 0xFFFFFFFF else x),
335-
0x07: BaseType(name='string', identifier=0x07, fmt='s', parse=lambda x: x[0:x.index(0)].decode('utf-8', errors='replace') or None),
348+
0x07: BaseType(name='string', identifier=0x07, fmt='s', parse=parse_string),
336349
0x88: BaseType(name='float32', identifier=0x88, fmt='f', parse=lambda x: None if math.isnan(x) else x),
337350
0x89: BaseType(name='float64', identifier=0x89, fmt='d', parse=lambda x: None if math.isnan(x) else x),
338351
0x0A: BaseType(name='uint8z', identifier=0x0A, fmt='B', parse=lambda x: None if x == 0x0 else x),

‎fitparse/utils.py‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,16 @@ class FitHeaderError(FitParseError):
2020
)
2121

2222

23-
def calc_crc(bytes, crc=0):
24-
for byte in bytes:
25-
byte_char = byte
23+
def calc_crc(byte_arr, crc=0):
24+
for byte in bytearray(byte_arr):
2625
# Taken verbatim from FIT SDK docs
2726
tmp = CRC_TABLE[crc & 0xF]
2827
crc = (crc >> 4) & 0x0FFF
29-
crc = crc ^ tmp ^ CRC_TABLE[byte_char & 0xF]
28+
crc = crc ^ tmp ^ CRC_TABLE[byte & 0xF]
3029

3130
tmp = CRC_TABLE[crc & 0xF]
3231
crc = (crc >> 4) & 0x0FFF
33-
crc = crc ^ tmp ^ CRC_TABLE[(byte_char >> 4) & 0xF]
32+
crc = crc ^ tmp ^ CRC_TABLE[(byte >> 4) & 0xF]
3433
return crc
3534

3635

‎scripts/fitdump‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
import argparse
44
import sys
55

6+
# Python 2 compat
7+
try:
8+
BrokenPipeError
9+
except NameError:
10+
import socket
11+
BrokenPipeError = socket.error
12+
613
import fitparse
714

815

‎tests/test.py‎

100644100755
Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
#!/usr/bin/env python
2+
13
import csv
24
import datetime
35
import os
46
from struct import pack
57
import sys
68

79
from fitparse import FitFile
8-
from fitparse.processors import UTC_REFERENCE
10+
from fitparse.processors import UTC_REFERENCE, StandardUnitsDataProcessor
911
from fitparse.records import BASE_TYPES
1012
from fitparse.utils import calc_crc, FitEOFError, FitCRCError, FitHeaderError
1113

@@ -109,13 +111,13 @@ def test_component_field_accumulaters(self):
109111
# TODO: abstract CSV parsing
110112
csv_fp = open(testfile('compressed-speed-distance-records.csv'), 'r')
111113
csv_file = csv.reader(csv_fp)
112-
csv_file.__next__() # Consume header
114+
next(csv_file) # Consume header
113115

114116
f = FitFile(testfile('compressed-speed-distance.fit'))
115117
f.parse()
116118

117119
records = f.get_messages(name='record')
118-
empty_record = records.__next__() # Skip empty record for now (sets timestamp via header)
120+
empty_record = next(records) # Skip empty record for now (sets timestamp via header)
119121

120122
# File's timestamp record is < 0x10000000, so field returns seconds
121123
self.assertEqual(empty_record.get_value('timestamp'), 17217864)
@@ -230,7 +232,7 @@ def test_subfield_components(self):
230232
def test_parsing_edge_500_fit_file(self):
231233
csv_fp = open(testfile('garmin-edge-500-activitiy-records.csv'), 'r')
232234
csv_messages = csv.reader(csv_fp)
233-
field_names = csv_messages.__next__() # Consume header
235+
field_names = next(csv_messages) # Consume header
234236

235237
f = FitFile(testfile('garmin-edge-500-activitiy.fit'))
236238
messages = f.get_messages(name='record')
@@ -274,13 +276,13 @@ def test_parsing_edge_500_fit_file(self):
274276
self.assertEqual(fit_value, csv_value)
275277

276278
try:
277-
messages.__next__()
279+
next(messages)
278280
self.fail(".FIT file had more than csv file")
279281
except StopIteration:
280282
pass
281283

282284
try:
283-
csv_messages.__next__()
285+
next(csv_messages)
284286
self.fail(".CSV file had more messages than .FIT file")
285287
except StopIteration:
286288
pass
@@ -346,6 +348,18 @@ def test_valid_files(self):
346348
'sample-activity.fit'):
347349
FitFile(testfile(x)).parse()
348350

351+
def test_units_processor(self):
352+
for x in ('2013-02-06-12-11-14.fit', '2015-10-13-08-43-15.fit',
353+
'Activity.fit', 'Edge810-Vector-2013-08-16-15-35-10.fit',
354+
'MonitoringFile.fit', 'Settings.fit', 'Settings2.fit',
355+
'WeightScaleMultiUser.fit', 'WeightScaleSingleUser.fit',
356+
'WorkoutCustomTargetValues.fit', 'WorkoutIndividualSteps.fit',
357+
'WorkoutRepeatGreaterThanStep.fit', 'WorkoutRepeatSteps.fit',
358+
'activity-large-fenxi2-multisport.fit', 'activity-small-fenix2-run.fit',
359+
'antfs-dump.63.fit', 'sample-activity-indoor-trainer.fit',
360+
'sample-activity.fit'):
361+
FitFile(testfile(x), data_processor=StandardUnitsDataProcessor()).parse()
362+
349363
# TODO:
350364
# * Test Processors:
351365
# - process_type_<>, process_field_<>, process_units_<>, process_message_<>

0 commit comments

Comments
 (0)