Skip to content

Commit acd5434

Browse files
committed
RFCT utils.fileish_open with a write possibility
1 parent 490e885 commit acd5434

File tree

4 files changed

+97
-29
lines changed

4 files changed

+97
-29
lines changed

fitparse/base.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,12 @@
1616
BASE_TYPES, BASE_TYPE_BYTE, DevField,
1717
add_dev_data_id, add_dev_field_description, get_dev_type
1818
)
19-
from fitparse.utils import calc_crc, FitParseError, FitEOFError, FitCRCError, FitHeaderError
19+
from fitparse.utils import calc_crc, fileish_open, FitParseError, FitEOFError, FitCRCError, FitHeaderError
20+
2021

2122
class FitFile(object):
2223
def __init__(self, fileish, check_crc=True, data_processor=None):
23-
if hasattr(fileish, 'read'):
24-
# BytesIO-like object
25-
self._file = fileish
26-
elif isinstance(fileish, str):
27-
# Python2 - file path, file contents in the case of a TypeError
28-
# Python3 - file path
29-
try:
30-
self._file = open(fileish, 'rb')
31-
except TypeError:
32-
self._file = io.BytesIO(fileish)
33-
else:
34-
# Python 3 - file contents
35-
self._file = io.BytesIO(fileish)
24+
self._file = fileish_open(fileish, 'rb')
3625

3726
self.check_crc = check_crc
3827
self._processor = data_processor or FitFileDataProcessor()

fitparse/utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import re
22

3+
import io
4+
35

46
class FitParseError(ValueError):
57
pass
@@ -47,3 +49,29 @@ def scrub_method_name(method_name, convert_units=False):
4749
replace_from, '%s' % replace_to,
4850
)
4951
return METHOD_NAME_SCRUBBER.sub('_', method_name)
52+
53+
54+
def fileish_open(fileish, mode):
55+
"""
56+
Convert file-ish object to BytesIO like object.
57+
:param fileish: the file-ihs object (str, BytesIO, bytes, file contents)
58+
:param str mode: mode for the open function.
59+
:rtype: BytesIO
60+
"""
61+
if mode is not None and any(m in mode for m in ['+', 'w', 'a', 'x']):
62+
attr = 'write'
63+
else:
64+
attr = 'read'
65+
if hasattr(fileish, attr) and hasattr(fileish, 'seek'):
66+
# BytesIO-like object
67+
return fileish
68+
elif isinstance(fileish, str):
69+
# Python2 - file path, file contents in the case of a TypeError
70+
# Python3 - file path
71+
try:
72+
return open(fileish, mode)
73+
except TypeError:
74+
return io.BytesIO(fileish)
75+
else:
76+
# Python 3 - file contents
77+
return io.BytesIO(fileish)

tests/test.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -398,21 +398,6 @@ def test_int_long(self):
398398
with FitFile(testfile('event_timestamp.fit')) as f:
399399
assert f.messages[-1].fields[1].raw_value == 1739.486328125
400400

401-
def test_fileish_types(self):
402-
"""Test the constructor does the right thing when given different types
403-
(specifically, test files with 8 characters, followed by an uppercase.FIT
404-
extension), which confused the fileish check on Python 2, see
405-
https://github.com/dtcooper/python-fitparse/issues/29#issuecomment-312436350
406-
for details"""
407-
with FitFile(testfile('nametest.FIT')):
408-
pass
409-
with open(testfile("nametest.FIT"), 'rb') as f:
410-
FitFile(f)
411-
with open(testfile("nametest.FIT"), 'rb') as f:
412-
FitFile(f.read())
413-
with open(testfile("nametest.FIT"), 'rb') as f:
414-
FitFile(io.BytesIO(f.read()))
415-
416401
def test_elemnt_bolt_developer_data_id_without_application_id(self):
417402
"""Test that a file without application id set inside developer_data_id is parsed
418403
(as seen on ELEMNT BOLT with firmware version WB09-1507)"""

tests/test_utils.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/env python
2+
3+
import io
4+
import os
5+
import sys
6+
import tempfile
7+
8+
from fitparse.utils import fileish_open
9+
10+
if sys.version_info >= (2, 7):
11+
import unittest
12+
else:
13+
import unittest2 as unittest
14+
15+
16+
def testfile(filename):
17+
return os.path.join(os.path.dirname(os.path.realpath(__file__)), 'files', filename)
18+
19+
20+
class UtilsTestCase(unittest.TestCase):
21+
22+
def test_fileish_open_read(self):
23+
"""Test the constructor does the right thing when given different types
24+
(specifically, test files with 8 characters, followed by an uppercase.FIT
25+
extension), which confused the fileish check on Python 2, see
26+
https://github.com/dtcooper/python-fitparse/issues/29#issuecomment-312436350
27+
for details"""
28+
29+
def test_fopen(fileish):
30+
with fileish_open(fileish, 'rb') as f:
31+
self.assertIsNotNone(f.read(1))
32+
f.seek(0, os.SEEK_SET)
33+
34+
test_fopen(testfile('nametest.FIT'))
35+
with open(testfile("nametest.FIT"), 'rb') as f:
36+
test_fopen(f)
37+
with open(testfile("nametest.FIT"), 'rb') as f:
38+
test_fopen(f.read())
39+
with open(testfile("nametest.FIT"), 'rb') as f:
40+
test_fopen(io.BytesIO(f.read()))
41+
42+
def test_fileish_open_write(self):
43+
44+
def test_fopen(fileish):
45+
with fileish_open(fileish, 'wb') as f:
46+
f.write(b'\x12')
47+
f.seek(0, os.SEEK_SET)
48+
49+
tmpfile = tempfile.NamedTemporaryFile(prefix='fitparse-test', suffix='.FIT', delete=False)
50+
filename = tmpfile.name
51+
tmpfile.close()
52+
try:
53+
test_fopen(filename)
54+
with open(filename, 'wb') as f:
55+
test_fopen(f)
56+
test_fopen(io.BytesIO())
57+
finally:
58+
# remove silently
59+
try:
60+
os.remove(filename)
61+
except OSError:
62+
pass
63+
64+
65+
if __name__ == '__main__':
66+
unittest.main()

0 commit comments

Comments
 (0)