Skip to content

Commit a4e61d7

Browse files
authored
eds: Style and type fixes around build_variable() (#661)
* eds: Centralize object type logic in build_variable. Pass object type to build_variable() instead of the boolean is_domain parameter. Add type hints to aid in validating all call sites have been adjusted. * Remove unnecessary duplicate lookup from ConfigParser. * Fix mypy errors in build_variable(). The storage_location attribute needs an explicit annotation (assumed only None type) on the OD variable classes, to accept strings from eds. Silence mypy in import_eds() for a custom attribute addition. * Respect line-length in build_variable + small style fix.
1 parent 929f4d4 commit a4e61d7

2 files changed

Lines changed: 34 additions & 20 deletions

File tree

canopen/objectdictionary/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ def __init__(self, name: str, index: int):
206206
#: Name of record
207207
self.name = name
208208
#: Storage location of index
209-
self.storage_location = None
209+
self.storage_location: Optional[str] = None
210210
self.subindices: dict[int, ODVariable] = {}
211211
self.names: dict[str, ODVariable] = {}
212212

@@ -267,7 +267,7 @@ def __init__(self, name: str, index: int):
267267
#: Name of array
268268
self.name = name
269269
#: Storage location of index
270-
self.storage_location = None
270+
self.storage_location: Optional[str] = None
271271
self.subindices: dict[int, ODVariable] = {}
272272
self.names: dict[str, ODVariable] = {}
273273

@@ -377,7 +377,7 @@ def __init__(self, name: str, index: int, subindex: int = 0):
377377
#: Dictionary of bitfield definitions
378378
self.bit_definitions: dict[str, list[int]] = {}
379379
#: Storage location of index
380-
self.storage_location = None
380+
self.storage_location: Optional[str] = None
381381
#: Can this variable be mapped to a PDO
382382
self.pdo_mappable = False
383383

canopen/objectdictionary/eds.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def import_eds(source, node_id):
4141
od = ObjectDictionary()
4242

4343
if eds.has_section("FileInfo"):
44-
od.__edsFileInfo = {
44+
od.__edsFileInfo = { # type: ignore[attr-defined] # custom addition
4545
opt: eds.get("FileInfo", opt)
4646
for opt in eds.options("FileInfo")
4747
}
@@ -50,7 +50,7 @@ def import_eds(source, node_id):
5050
linecount = int(eds.get("Comments", "Lines"), 0)
5151
od.comments = '\n'.join([
5252
eds.get("Comments", f"Line{line}")
53-
for line in range(1, linecount+1)
53+
for line in range(1, linecount + 1)
5454
])
5555

5656
if not eds.has_section("DeviceInfo"):
@@ -129,15 +129,15 @@ def import_eds(source, node_id):
129129
storage_location = None
130130

131131
if object_type in (objectcodes.VAR, objectcodes.DOMAIN):
132-
var = build_variable(eds, section, node_id, index, is_domain=object_type == objectcodes.DOMAIN)
132+
var = build_variable(eds, section, node_id, object_type, index)
133133
od.add_object(var)
134134
elif object_type == objectcodes.ARRAY and eds.has_option(section, "CompactSubObj"):
135135
arr = ODArray(name, index)
136136
last_subindex = ODVariable(
137137
"Number of entries", index, 0)
138138
last_subindex.data_type = datatypes.UNSIGNED8
139139
arr.add_member(last_subindex)
140-
arr.add_member(build_variable(eds, section, node_id, index, 1))
140+
arr.add_member(build_variable(eds, section, node_id, object_type, index, 1))
141141
arr.storage_location = storage_location
142142
od.add_object(arr)
143143
elif object_type == objectcodes.ARRAY:
@@ -162,7 +162,7 @@ def import_eds(source, node_id):
162162
object_type = int(eds.get(section, "ObjectType"), 0)
163163
except NoOptionError:
164164
object_type = objectcodes.VAR
165-
var = build_variable(eds, section, node_id, index, subindex, is_domain=object_type == objectcodes.DOMAIN)
165+
var = build_variable(eds, section, node_id, object_type, index, subindex)
166166
entry.add_member(var)
167167

168168
# Match [index]Name
@@ -214,7 +214,9 @@ def _calc_bit_length(data_type):
214214
elif data_type == datatypes.INTEGER64:
215215
return 64
216216
else:
217-
raise ValueError(f"Invalid data_type '{data_type}', expecting a signed integer data_type.")
217+
raise ValueError(
218+
f"Invalid data_type '{data_type}', expecting a signed integer data_type."
219+
)
218220

219221

220222
def _signed_int_from_hex(hex_str, bit_length):
@@ -256,8 +258,16 @@ def _revert_variable(var_type, value):
256258
return f"0x{value:02X}"
257259

258260

259-
def build_variable(eds, section, node_id, index, subindex=0, is_domain=False):
260-
"""Creates a object dictionary entry.
261+
def build_variable(
262+
eds: RawConfigParser,
263+
section: str,
264+
node_id: int,
265+
object_type: int,
266+
index: int,
267+
subindex: int = 0
268+
) -> ODVariable:
269+
"""Create a object dictionary entry.
270+
261271
:param eds: String stream of the eds file
262272
:param section:
263273
:param node_id: Node ID
@@ -273,16 +283,19 @@ def build_variable(eds, section, node_id, index, subindex=0, is_domain=False):
273283
var.storage_location = None
274284
var.data_type = int(eds.get(section, "DataType"), 0)
275285
var.access_type = eds.get(section, "AccessType").lower()
276-
var.is_domain = is_domain
286+
var.is_domain = object_type == objectcodes.DOMAIN
277287
if var.data_type > 0x1B:
278-
# The object dictionary editor from CANFestival creates an optional object if min max values are used
279-
# This optional object is then placed in the eds under the section [A0] (start point, iterates for more)
280-
# The eds.get function gives us 0x00A0 now convert to String without hex representation and upper case
281-
# The sub2 part is then the section where the type parameter stands
288+
# The object dictionary editor from CANFestival creates an optional object if min max
289+
# values are used. This optional object is then placed in the eds under the section
290+
# [A0] (start point, iterates for more). The eds.get function gives us 0x00A0 now
291+
# convert to String without hex representation and upper case. The sub2 part is then
292+
# the section where the type parameter stands.
282293
try:
283294
var.data_type = int(eds.get(f"{var.data_type:X}sub1", "DefaultValue"), 0)
284295
except NoSectionError:
285-
logger.warning("%s has an unknown or unsupported data type (0x%X)", name, var.data_type)
296+
logger.warning(
297+
"%s has an unknown or unsupported data type (0x%X)", name, var.data_type
298+
)
286299
# Assume DOMAIN to force application to interpret the byte data
287300
var.data_type = datatypes.DOMAIN
288301

@@ -311,16 +324,17 @@ def build_variable(eds, section, node_id, index, subindex=0, is_domain=False):
311324
var.default_raw = eds.get(section, "DefaultValue")
312325
if '$NODEID' in var.default_raw:
313326
var.relative = True
314-
var.default = _convert_variable(node_id, var.data_type, eds.get(section, "DefaultValue"))
327+
var.default = _convert_variable(node_id, var.data_type, var.default_raw)
315328
except ValueError:
316329
pass
317330
if eds.has_option(section, "ParameterValue"):
318331
try:
319332
var.value_raw = eds.get(section, "ParameterValue")
320-
var.value = _convert_variable(node_id, var.data_type, eds.get(section, "ParameterValue"))
333+
var.value = _convert_variable(node_id, var.data_type, var.value_raw)
321334
except ValueError:
322335
pass
323-
# Factor, Description and Unit are not standard according to the CANopen specifications, but they are implemented in the python canopen package, so we can at least try to use them
336+
# Factor, Description and Unit are not standard according to the CANopen specifications, but
337+
# they are implemented in the python canopen package, so we can at least try to use them
324338
if eds.has_option(section, "Factor"):
325339
try:
326340
var.factor = float(eds.get(section, "Factor"))

0 commit comments

Comments
 (0)