eds: fix LowLimit/HighLimit parsing for all signed integer types#658
eds: fix LowLimit/HighLimit parsing for all signed integer types#658friederschueler wants to merge 2 commits intocanopen-python:masterfrom
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
Please always separate style changes from functional changes. Except of course fixing in the immediate vicinity of the functional changes. |
131f43e to
27e94d8
Compare
| _SIGNED_BIT_LENGTHS = { | ||
| datatypes.INTEGER8: 8, | ||
| datatypes.INTEGER16: 16, | ||
| datatypes.INTEGER24: 24, | ||
| datatypes.INTEGER32: 32, | ||
| datatypes.INTEGER40: 40, | ||
| datatypes.INTEGER48: 48, | ||
| datatypes.INTEGER56: 56, | ||
| datatypes.INTEGER64: 64, | ||
| } |
There was a problem hiding this comment.
We don't need to hard-code these at all:
| _SIGNED_BIT_LENGTHS = { | |
| datatypes.INTEGER8: 8, | |
| datatypes.INTEGER16: 16, | |
| datatypes.INTEGER24: 24, | |
| datatypes.INTEGER32: 32, | |
| datatypes.INTEGER40: 40, | |
| datatypes.INTEGER48: 48, | |
| datatypes.INTEGER56: 56, | |
| datatypes.INTEGER64: 64, | |
| } | |
| def _calc_bit_length(data_type: int) -> int: | |
| if data_type in datatypes.SIGNED_TYPES: | |
| st = ODVariable.STRUCT_TYPES[data_type] | |
| if isinstance(st, datatypes.IntegerN): | |
| return st.width | |
| return st.size * 8 | |
| else: | |
| raise ValueError( | |
| f"Invalid data_type '{data_type}', expecting a signed integer data_type." | |
| ) |
There was a problem hiding this comment.
Not a big fan of the function, following the zen of python:
simple is better than complex
explicit is better than implicit
a lookup dict is the way to go. one could argue to put it into datatypes.py
There was a problem hiding this comment.
In this case I'm more convinced by the "Don't Repeat Yourself" principle. Listing combinations of numbers and a symbol including the same number, sounds pretty useless. As the data is already there, I prefer a helper function in the object_dictionary module, instead of duplicating it here. A dict can be generated as well from the existing data, but it is really a waste of memory 99.9 % of the time.
What makes this function a bit complicated is the fact that the dict in class ODVariable uses two different approaches for different bit widths. But that is really material for another round of cleanup, maybe changing the way we generate that dict instead of re-using it elsewhere.
Please go with the function to not add technical debt here.
| var.min = int(min_string, 0) | ||
| except ValueError: | ||
| pass | ||
| logger.warning("Failed to parse LowLimit for %s: %r", name, min_string) |
There was a problem hiding this comment.
Logging is better than not logging, of course. But this whole treatment looks suspicious:
- How does the outside code even notice that the EDS partly failed to parse? Informing the user for troubleshooting is not enough IMHO, but raising would be a massive API break, causing potential errors with previously "working" EDS files.
- AFAICT the _signed_int_from_hex() helper does not even fully check whether the value is representable in the given bit width. That's another class of EDS errors we could easily catch and report here, but don't.
- Ignoring
ValueErrorseems like a pattern blindly applied to every option. Even forDescriptionandUnitbelow, which are strings and there is not even a chance of aValueErrorfromConfigParser.get().
When we touch / fix this code, let's put some more thought into what really makes sense. Especially point 1 is not solved by logging alone. Being stricter in parsing EDS files might be beneficial.
There was a problem hiding this comment.
Ok, I get the point and tend to agree to just don't touch it at this point.
There was a problem hiding this comment.
What I really meant was to fix it now. But of course it can be another PR, to keep this focused. You up to it, or should I?
- Replace _calc_bit_length() (only handled INTEGER8/16/32/64) with a lookup dict _SIGNED_BIT_LENGTHS covering all 8 SIGNED_TYPES (INTEGER8/16/24/32/40/48/56/64) - Log a warning instead of silently ignoring invalid limit values (except ValueError: pass) - Add EDS test entries in sample.eds for INTEGER24/40/48/56 with hex-encoded negative limits - Extend test_record_with_limits and add test_invalid_limit_logs_warning Closes part of canopen-python#352.
27e94d8 to
f65a1fb
Compare
Fixes incomplete signed integer handling for
LowLimit/HighLimitin EDS parsing, as noted in #352.Changes
_calc_bit_length()(only handled INTEGER8/16/32/64) with a lookup dict_SIGNED_BIT_LENGTHScovering all 8SIGNED_TYPES(INTEGER8/16/24/32/40/48/56/64)warninginstead of silently ignoring invalid limit values (except ValueError: pass)sample.edsfor INTEGER24/40/48/56 with hex-encoded negative limitstest_record_with_limitsand addtest_invalid_limit_logs_warningNote on formatting
eds.pyandtest_eds.pywere reformatted with black. If a purely functional diff is preferred, the formatting commits can be dropped — the functional changes are small and easy to separate.Closes part of #352.