Sourcery refactored master branch#1
Conversation
| raise FitParseError("No such dev_data_index=%s found" % (dev_data_index)) | ||
| raise FitParseError(f"No such dev_data_index={dev_data_index} found") | ||
|
|
||
| warnings.warn( | ||
| "Dev type for dev_data_index=%s missing. Adding dummy dev type." % (dev_data_index) | ||
| f"Dev type for dev_data_index={dev_data_index} missing. Adding dummy dev type." | ||
| ) | ||
|
|
There was a problem hiding this comment.
Function DeveloperDataMixin._append_dev_field_description refactored with the following changes:
- Replace interpolated string formatting with f-string [×2] (
replace-interpolation-with-fstring)
| field_name = message.get_raw_value('field_name') or "unnamed_dev_field_%s" % field_def_num | ||
| field_name = ( | ||
| message.get_raw_value('field_name') | ||
| or f"unnamed_dev_field_{field_def_num}" | ||
| ) | ||
|
|
||
| units = message.get_raw_value("units") | ||
| native_field_num = message.get_raw_value('native_field_num') | ||
|
|
||
| if dev_data_index not in self.dev_types: | ||
| if self.check_developer_data: | ||
| raise FitParseError("No such dev_data_index=%s found" % (dev_data_index)) | ||
| raise FitParseError(f"No such dev_data_index={dev_data_index} found") | ||
|
|
||
| warnings.warn( | ||
| "Dev type for dev_data_index=%s missing. Adding dummy dev type." % (dev_data_index) | ||
| f"Dev type for dev_data_index={dev_data_index} missing. Adding dummy dev type." | ||
| ) | ||
|
|
There was a problem hiding this comment.
Function DeveloperDataMixin.add_dev_field_description refactored with the following changes:
- Replace interpolated string formatting with f-string [×3] (
replace-interpolation-with-fstring)
| "Dev type for dev_data_index=%s missing. Adding dummy dev type." % (dev_data_index) | ||
| f"Dev type for dev_data_index={dev_data_index} missing. Adding dummy dev type." | ||
| ) | ||
|
|
There was a problem hiding this comment.
Function DeveloperDataMixin.get_dev_type refactored with the following changes:
- Replace interpolated string formatting with f-string (
replace-interpolation-with-fstring)
| raise FitParseError("Invalid struct format: %s" % fmt_with_endian) | ||
| raise FitParseError(f"Invalid struct format: {fmt_with_endian}") |
There was a problem hiding this comment.
Function FitFileDecoder._read_struct refactored with the following changes:
- Replace interpolated string formatting with f-string (
replace-interpolation-with-fstring)
| raise FitCRCError('CRC Mismatch [computed: {}, read: {}]'.format( | ||
| Crc.format(crc_computed), Crc.format(crc_read))) | ||
| raise FitCRCError( | ||
| f'CRC Mismatch [computed: {Crc.format(crc_computed)}, read: {Crc.format(crc_read)}]' | ||
| ) |
There was a problem hiding this comment.
Function FitFileDecoder._read_and_assert_crc refactored with the following changes:
- Replace call to format with f-string (
use-fstring-for-formatting)
| if self.field: | ||
| if name in (self.field.name, self.field.def_num): | ||
| return True | ||
| if self.parent_field: | ||
| if name in (self.parent_field.name, self.parent_field.def_num): | ||
| return True | ||
| if self.field_def: | ||
| if name == self.field_def.def_num: | ||
| return True | ||
| return False | ||
| if self.field and name in (self.field.name, self.field.def_num): | ||
| return True | ||
| if self.parent_field and name in ( | ||
| self.parent_field.name, | ||
| self.parent_field.def_num, | ||
| ): | ||
| return True | ||
| return bool(self.field_def and name == self.field_def.def_num) |
There was a problem hiding this comment.
Function FieldData.is_named refactored with the following changes:
- Merge nested if conditions [×3] (
merge-nested-ifs) - Simplify boolean if expression (
boolean-if-exp-identity) - Lift code into else after jump in control flow (
reintroduce-else) - Replace if statement with if expression (
assign-if-exp)
| return '<FieldData: %s: %s%s, def num: %d, type: %s (%s), raw value: %s>' % ( | ||
| self.name, self.value, ' [%s]' % self.units if self.units else '', | ||
| self.def_num, self.type.name, self.base_type.name, self.raw_value, | ||
| return ( | ||
| '<FieldData: %s: %s%s, def num: %d, type: %s (%s), raw value: %s>' | ||
| % ( | ||
| self.name, | ||
| self.value, | ||
| f' [{self.units}]' if self.units else '', | ||
| self.def_num, | ||
| self.type.name, | ||
| self.base_type.name, | ||
| self.raw_value, | ||
| ) |
There was a problem hiding this comment.
Function FieldData.__repr__ refactored with the following changes:
- Replace interpolated string formatting with f-string (
replace-interpolation-with-fstring)
| return '{}: {}{}'.format( | ||
| self.name, self.value, ' [%s]' % self.units if self.units else '', | ||
| ) | ||
| return f"{self.name}: {self.value}{f' [{self.units}]' if self.units else ''}" |
There was a problem hiding this comment.
Function FieldData.__str__ refactored with the following changes:
- Replace call to format with f-string (
use-fstring-for-formatting) - Replace interpolated string formatting with f-string (
replace-interpolation-with-fstring)
| return '<{} {}>'.format(self.__class__.__name__, self.value or "-") | ||
| return f'<{self.__class__.__name__} {self.value or "-"}>' |
There was a problem hiding this comment.
Function Crc.__repr__ refactored with the following changes:
- Replace call to format with f-string (
use-fstring-for-formatting)
| method_name = method_name.replace( | ||
| replace_from, '%s' % replace_to, | ||
| ) | ||
| method_name = method_name.replace(replace_from, f'{replace_to}') |
There was a problem hiding this comment.
Function scrub_method_name refactored with the following changes:
- Replace interpolated string formatting with f-string (
replace-interpolation-with-fstring)
Sourcery Code Quality Report❌ Merging this PR will decrease code quality in the affected files by 0.12%.
Here are some functions in these files that still need a tune-up:
Legend and ExplanationThe emojis denote the absolute quality of the code:
The 👍 and 👎 indicate whether the quality has improved or gotten worse with this pull request. Please see our documentation here for details on how these metrics are calculated. We are actively working on this report - lots more documentation and extra metrics to come! Help us improve this quality report! |
| epilog='python-fitparse version %s' % fitparse.__version__, | ||
| epilog=f'python-fitparse version {fitparse.__version__}', | ||
| ) | ||
|
|
There was a problem hiding this comment.
Function parse_args refactored with the following changes:
- Replace interpolated string formatting with f-string (
replace-interpolation-with-fstring)
|
|
||
| def header(header, indent=0): | ||
| return '{}# {}'.format(' ' * indent, (' %s ' % header).center(78 - indent, '*')) | ||
| return f"{' ' * indent}# {f' {header} '.center(78 - indent, '*')}" |
There was a problem hiding this comment.
Function header refactored with the following changes:
- Replace call to format with f-string (
use-fstring-for-formatting) - Replace interpolated string formatting with f-string (
replace-interpolation-with-fstring)
| PROFILE_HEADER_FIRST_PART = "{}\n{}".format( | ||
| header('BEGIN AUTOMATICALLY GENERATED FIT PROFILE'), | ||
| header('DO NOT EDIT THIS FILE'), | ||
| ) | ||
| PROFILE_HEADER_FIRST_PART = f"{header('BEGIN AUTOMATICALLY GENERATED FIT PROFILE')}\n{header('DO NOT EDIT THIS FILE')}" | ||
|
|
There was a problem hiding this comment.
Lines 38-41 refactored with the following changes:
- Replace call to format with f-string (
use-fstring-for-formatting)
| return "BASE_TYPES[{}], # {}".format(BASE_TYPES[name], name) | ||
| return f"BASE_TYPES[{BASE_TYPES[name]}], # {name}" |
There was a problem hiding this comment.
Function render_type refactored with the following changes:
- Replace call to format with f-string (
use-fstring-for-formatting)
| s += " '{}': {},\n".format(type.name, indent(type)) | ||
| s += f" '{type.name}': {indent(type)},\n" |
There was a problem hiding this comment.
Function TypeList.__str__ refactored with the following changes:
- Replace call to format with f-string (
use-fstring-for-formatting)
| if isinstance(value, float): | ||
| if value.is_integer(): | ||
| value = int(value) | ||
| if isinstance(value, float) and value.is_integer(): | ||
| value = int(value) | ||
|
|
||
| values.append(value) | ||
|
|
||
| if all(v is None for v in values): | ||
| continue | ||
|
|
||
| parsed_values.append(values) | ||
| if any(v is not None for v in values): | ||
| parsed_values.append(values) |
There was a problem hiding this comment.
Function parse_spreadsheet refactored with the following changes:
- Merge nested if conditions (
merge-nested-ifs) - Lift code into else after jump in control flow (
reintroduce-else) - Remove redundant continue statement (
remove-redundant-continue) - Invert any/all to simplify comparisons (
invert-any-all)
| if value.name and value.value is not None: | ||
| # Don't add ignore keyed types | ||
| if "{}:{}".format(type.name, value.name) not in IGNORE_TYPE_VALUES: | ||
| type.values.append(value) | ||
| if ( | ||
| value.name | ||
| and value.value is not None | ||
| and f"{type.name}:{value.name}" not in IGNORE_TYPE_VALUES | ||
| ): | ||
| type.values.append(value) |
There was a problem hiding this comment.
Function parse_types refactored with the following changes:
- Merge nested if conditions (
merge-nested-ifs) - Replace call to format with f-string (
use-fstring-for-formatting)
This removes the following comments ( why? ):
# Don't add ignore keyed types
| if isinstance(o, bytes): | ||
| return o.decode() | ||
| return o | ||
| return o.decode() if isinstance(o, bytes) else o |
There was a problem hiding this comment.
Function maybe_decode refactored with the following changes:
- Lift code into else after jump in control flow (
reintroduce-else) - Replace if statement with if expression (
assign-if-exp)
| version_match = re.search( | ||
| if version_match := re.search( | ||
| r'Profile Version.+?(\d+\.?\d*).*', | ||
| archive.open('c/fit.h').read().decode(), | ||
| ) | ||
| if version_match: | ||
| profile_version = ("%f" % float(version_match.group(1))).rstrip('0').ljust(4, '0') | ||
| ): | ||
| profile_version = ("%f" % float(version_match[1])).rstrip('0').ljust(4, '0') |
There was a problem hiding this comment.
Function get_xls_and_version_from_zip refactored with the following changes:
- Use named expression to simplify assignment and conditional (
use-named-expression) - Replace m.group(x) with m[x] for re.Match objects (
use-getitem-for-re-match-groups)
| if output_py_path and os.path.exists(output_py_path): | ||
| if not open(output_py_path).read().strip().startswith(PROFILE_HEADER_FIRST_PART): | ||
| print("Python file doesn't begin with appropriate header. Exiting.") | ||
| sys.exit(1) | ||
| if ( | ||
| output_py_path | ||
| and os.path.exists(output_py_path) | ||
| and not open(output_py_path) | ||
| .read() | ||
| .strip() | ||
| .startswith(PROFILE_HEADER_FIRST_PART) | ||
| ): | ||
| print("Python file doesn't begin with appropriate header. Exiting.") | ||
| sys.exit(1) |
There was a problem hiding this comment.
Function main refactored with the following changes:
- Merge nested if conditions (
merge-nested-ifs) - Replace call to format with f-string [×4] (
use-fstring-for-formatting) - Use f-string instead of string concatenation (
use-fstring-for-concatenation) - Simplify if expression by using or (
or-if-exp-identity) - Replace interpolated string formatting with f-string (
replace-interpolation-with-fstring)
| print("Usage: %s <FitSDK.zip | Profile.xls> [profile.py]" % os.path.basename(__file__)) | ||
| print( | ||
| f"Usage: {os.path.basename(__file__)} <FitSDK.zip | Profile.xls> [profile.py]" | ||
| ) | ||
|
|
There was a problem hiding this comment.
Lines 621-621 refactored with the following changes:
- Replace interpolated string formatting with f-string (
replace-interpolation-with-fstring)
| unit_value = unit_value.strip() | ||
| if unit_value: | ||
| if unit_value := unit_value.strip(): | ||
| # Deal with comma separated components | ||
| unit_values = [v.strip() for v in unit_value.split(',')] | ||
| all_unit_values.extend(unit_values) | ||
|
|
||
| print('In Profile.xls:') | ||
| for unit_value in sorted(set(all_unit_values)): | ||
| print(' * %s' % unit_value) | ||
| print(f' * {unit_value}') |
There was a problem hiding this comment.
Function do_profile_xls refactored with the following changes:
- Use named expression to simplify assignment and conditional (
use-named-expression) - Replace interpolated string formatting with f-string (
replace-interpolation-with-fstring)
| for component in field.components: | ||
| unit_values.append(component.units) | ||
| unit_values.extend(component.units for component in field.components) | ||
| if field.subfields: | ||
| for subfield in field.subfields: | ||
| unit_values.append(subfield.units) | ||
| if subfield.components: | ||
| for component in subfield.components: | ||
| unit_values.append(component.units) | ||
|
|
||
| unit_values.extend(component.units for component in subfield.components) | ||
| unit_values = filter(None, unit_values) | ||
|
|
||
| print('In fitparse/profile.py:') | ||
| for unit_value in sorted(set(unit_values)): | ||
| print(' * {} [{}]'.format( | ||
| unit_value, | ||
| scrub_method_name('process_units_%s' % unit_value, convert_units=True) | ||
| )) | ||
| print( | ||
| ' * {} [{}]'.format( | ||
| unit_value, | ||
| scrub_method_name( | ||
| f'process_units_{unit_value}', convert_units=True | ||
| ), | ||
| ) | ||
| ) |
There was a problem hiding this comment.
Function do_fitparse_profile refactored with the following changes:
- Replace a for append loop with list extend [×2] (
for-append-to-extend) - Replace interpolated string formatting with f-string (
replace-interpolation-with-fstring)
|
|
||
|
|
||
| def generate_messages(mesg_num, local_mesg_num, field_defs, endian='<', data=None): | ||
| mesgs = [] |
There was a problem hiding this comment.
Function generate_messages refactored with the following changes:
- Move assignment closer to its usage within a block (
move-assign-in-block) - Replace interpolated string formatting with f-string (
replace-interpolation-with-fstring) - Replace call to format with f-string (
use-fstring-for-formatting) - Merge append into list declaration (
merge-list-append)
| file_data = header + pack('<' + Crc.FMT, Crc.calculate(header)) + fit_data | ||
| return file_data + pack('<' + Crc.FMT, Crc.calculate(file_data)) | ||
| file_data = header + pack(f'<{Crc.FMT}', Crc.calculate(header)) + fit_data | ||
| return file_data + pack(f'<{Crc.FMT}', Crc.calculate(file_data)) |
There was a problem hiding this comment.
Function generate_fitfile refactored with the following changes:
- Use f-string instead of string concatenation [×2] (
use-fstring-for-concatenation)
| # TODO: abstract CSV parsing | ||
| csv_fp = open(testfile('compressed-speed-distance-records.csv')) | ||
| csv_file = csv.reader(csv_fp) | ||
| next(csv_file) # Consume header | ||
| with open(testfile('compressed-speed-distance-records.csv')) as csv_fp: | ||
| csv_file = csv.reader(csv_fp) | ||
| next(csv_file) # Consume header | ||
|
|
||
| f = FitFile(testfile('compressed-speed-distance.fit')) | ||
| f.parse() | ||
| f = FitFile(testfile('compressed-speed-distance.fit')) | ||
| f.parse() | ||
|
|
||
| records = f.get_messages(name='record') | ||
| empty_record = next(records) # Skip empty record for now (sets timestamp via header) | ||
| records = f.get_messages(name='record') | ||
| empty_record = next(records) # Skip empty record for now (sets timestamp via header) | ||
|
|
||
| # File's timestamp record is < 0x10000000, so field returns seconds | ||
| self.assertEqual(empty_record.get_value('timestamp'), 17217864) | ||
| # File's timestamp record is < 0x10000000, so field returns seconds | ||
| self.assertEqual(empty_record.get_value('timestamp'), 17217864) | ||
|
|
||
| # TODO: update using local_timestamp as offset, since we have this value as 2012 date | ||
| # TODO: update using local_timestamp as offset, since we have this value as 2012 date | ||
|
|
||
| for count, (record, (timestamp, heartrate, speed, distance, cadence)) in enumerate(zip(records, csv_file)): | ||
| # No fancy datetime stuff, since timestamp record is < 0x10000000 | ||
| fit_ts = record.get_value('timestamp') | ||
| self.assertIsInstance(fit_ts, int) | ||
| self.assertLess(fit_ts, 0x10000000) | ||
| self.assertEqual(fit_ts, int(timestamp)) | ||
| for count, (record, (timestamp, heartrate, speed, distance, cadence)) in enumerate(zip(records, csv_file)): | ||
| # No fancy datetime stuff, since timestamp record is < 0x10000000 | ||
| fit_ts = record.get_value('timestamp') | ||
| self.assertIsInstance(fit_ts, int) | ||
| self.assertLess(fit_ts, 0x10000000) | ||
| self.assertEqual(fit_ts, int(timestamp)) | ||
|
|
||
| self.assertEqual(record.get_value('heart_rate'), int(heartrate)) | ||
| self.assertEqual(record.get_value('cadence'), int(cadence) if cadence != 'null' else None) | ||
| self.assertAlmostEqual(record.get_value('speed'), float(speed)) | ||
| self.assertAlmostEqual(record.get_value('distance'), float(distance)) | ||
| self.assertEqual(record.get_value('heart_rate'), int(heartrate)) | ||
| self.assertEqual(record.get_value('cadence'), int(cadence) if cadence != 'null' else None) | ||
| self.assertAlmostEqual(record.get_value('speed'), float(speed)) | ||
| self.assertAlmostEqual(record.get_value('distance'), float(distance)) | ||
|
|
||
| self.assertEqual(count, 753) # TODO: confirm size(records) = size(csv) | ||
| csv_fp.close() | ||
| self.assertEqual(count, 753) # TODO: confirm size(records) = size(csv) |
There was a problem hiding this comment.
Function FitFileTestCase.test_component_field_accumulaters refactored with the following changes:
- Use
withwhen opening file to ensure closure (ensure-file-closed)
This removes the following comments ( why? ):
# TODO: abstract CSV parsing
| csv_fp = open(testfile(csv_file)) | ||
| csv_messages = csv.reader(csv_fp) | ||
| field_names = next(csv_messages) # Consume header | ||
|
|
||
| f = FitFile(testfile(fit_file)) | ||
| messages = f.get_messages(name='record') | ||
|
|
||
| # For fixups | ||
| last_valid_lat, last_valid_long = None, None | ||
|
|
||
| for message, csv_message in zip(messages, csv_messages): | ||
| for csv_index, field_name in enumerate(field_names): | ||
| fit_value, csv_value = message.get_value(field_name), csv_message[csv_index] | ||
| if field_name == 'timestamp': | ||
| # Adjust GMT to PDT and format | ||
| fit_value = (fit_value - datetime.timedelta(hours=7)).strftime("%a %b %d %H:%M:%S PDT %Y") | ||
|
|
||
| # Track last valid lat/longs | ||
| if field_name == 'position_lat': | ||
| if fit_value is not None: | ||
| with open(testfile(csv_file)) as csv_fp: | ||
| csv_messages = csv.reader(csv_fp) | ||
| field_names = next(csv_messages) # Consume header | ||
|
|
||
| f = FitFile(testfile(fit_file)) | ||
| messages = f.get_messages(name='record') | ||
|
|
||
| # For fixups | ||
| last_valid_lat, last_valid_long = None, None | ||
|
|
||
| for message, csv_message in zip(messages, csv_messages): | ||
| for csv_index, field_name in enumerate(field_names): | ||
| fit_value, csv_value = message.get_value(field_name), csv_message[csv_index] | ||
| if field_name == 'timestamp': | ||
| # Adjust GMT to PDT and format | ||
| fit_value = (fit_value - datetime.timedelta(hours=7)).strftime("%a %b %d %H:%M:%S PDT %Y") | ||
|
|
||
| # Track last valid lat/longs | ||
| if field_name == 'position_lat' and fit_value is not None: | ||
| last_valid_lat = fit_value | ||
| if field_name == 'position_long': | ||
| if fit_value is not None: | ||
| if field_name == 'position_long' and fit_value is not None: | ||
| last_valid_long = fit_value | ||
|
|
||
| # ANT FIT SDK Dump tool does a bad job of logging invalids, so fix them | ||
| if fit_value is None: | ||
| # ANT FIT SDK Dump tool cadence reports invalid as 0 | ||
| if field_name == 'cadence' and csv_value == '0': | ||
| # ANT FIT SDK Dump tool does a bad job of logging invalids, so fix them | ||
| if fit_value is None: | ||
| # ANT FIT SDK Dump tool cadence reports invalid as 0 | ||
| if field_name == 'cadence' and csv_value == '0': | ||
| csv_value = None | ||
| # ANT FIT SDK Dump tool invalid lat/lng reports as last valid | ||
| if field_name == 'position_lat': | ||
| fit_value = last_valid_lat | ||
| if field_name == 'position_long': | ||
| fit_value = last_valid_long | ||
|
|
||
| if isinstance(fit_value, int): | ||
| csv_value = int(fit_value) | ||
| if csv_value == '': | ||
| csv_value = None | ||
| # ANT FIT SDK Dump tool invalid lat/lng reports as last valid | ||
| if field_name == 'position_lat': | ||
| fit_value = last_valid_lat | ||
| if field_name == 'position_long': | ||
| fit_value = last_valid_long | ||
|
|
||
| if isinstance(fit_value, int): | ||
| csv_value = int(fit_value) | ||
| if csv_value == '': | ||
| csv_value = None | ||
|
|
||
| if isinstance(fit_value, float): | ||
| # Float comparison | ||
| self.assertAlmostEqual(fit_value, float(csv_value)) | ||
| else: | ||
| self.assertEqual(fit_value, csv_value, | ||
| msg="For {}, FIT value '{}' did not match CSV value '{}'".format(field_name, fit_value, csv_value)) | ||
|
|
||
| try: | ||
| next(messages) | ||
| self.fail(".FIT file had more than csv file") | ||
| except StopIteration: | ||
| pass | ||
|
|
||
| try: | ||
| next(csv_messages) | ||
| self.fail(".CSV file had more messages than .FIT file") | ||
| except StopIteration: | ||
| pass | ||
|
|
||
| csv_fp.close() | ||
| if isinstance(fit_value, float): | ||
| # Float comparison | ||
| self.assertAlmostEqual(fit_value, float(csv_value)) | ||
| else: | ||
| self.assertEqual( | ||
| fit_value, | ||
| csv_value, | ||
| msg=f"For {field_name}, FIT value '{fit_value}' did not match CSV value '{csv_value}'", | ||
| ) | ||
|
|
||
|
|
||
| try: | ||
| next(messages) | ||
| self.fail(".FIT file had more than csv file") | ||
| except StopIteration: | ||
| pass | ||
|
|
||
| try: | ||
| next(csv_messages) | ||
| self.fail(".CSV file had more messages than .FIT file") | ||
| except StopIteration: | ||
| pass |
There was a problem hiding this comment.
Function FitFileTestCase._csv_test_helper refactored with the following changes:
- Use
withwhen opening file to ensure closure (ensure-file-closed) - Merge nested if conditions [×2] (
merge-nested-ifs) - Replace call to format with f-string (
use-fstring-for-formatting)
Branch
masterrefactored by Sourcery.If you're happy with these changes, merge this Pull Request using the Squash and merge strategy.
See our documentation here.
Run Sourcery locally
Reduce the feedback loop during development by using the Sourcery editor plugin:
Review changes via command line
To manually merge these changes, make sure you're on the
masterbranch, then run:Help us improve this pull request!