Skip to content

Commit a693811

Browse files
authored
Move functions (#53)
* Bump version to 3.5.3a1 (developoment) * Move functions from jsonod.py to utils.py * Move functions from node.py to printing.py
1 parent 609ae6a commit a693811

File tree

10 files changed

+339
-261
lines changed

10 files changed

+339
-261
lines changed

src/objdictgen/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
from objdictgen.node import Node
2525
from objdictgen.nodemanager import NodeManager
2626

27-
__version__ = "3.5.2"
28-
__version_tuple__ = (3, 5, 2, 0)
27+
__version__ = "3.5.3a1"
28+
__version_tuple__ = (3, 5, 3, 1)
2929
__copyright__ = "(c) 2024 Svein Seldal, Laerdal Medical AS, and several. Licensed under GPLv2.1."
3030

3131
# Shortcuts

src/objdictgen/__main__.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import objdictgen
3232
from objdictgen import jsonod
33+
from objdictgen.printing import GetPrintEntry
3334
from objdictgen.typing import TDiffEntries, TDiffNodes, TPath
3435

3536
T = TypeVar('T')
@@ -168,8 +169,8 @@ def list_od(
168169
yield ""
169170

170171
# Print the parameters
171-
yield from od.GetPrintEntry(
172-
keys=keys, short=opts.short, compact=opts.compact, unused=opts.unused,
172+
yield from GetPrintEntry(
173+
od, keys=keys, short=opts.short, compact=opts.compact, unused=opts.unused,
173174
verbose=opts.all, raw=opts.raw
174175
)
175176

src/objdictgen/jsonod.py

+2-47
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
from objdictgen.maps import OD, ODMapping, ODMappingList
3636
from objdictgen.typing import (TDiffNodes, TIndexEntry, TODJson, TODObjJson,
3737
TODObj, TODSubObj, TODSubObjJson, TODValue, TParamEntry, TPath, TProfileMenu)
38+
from objdictgen.utils import (copy_in_order, exc_amend, maybe_number,
39+
str_to_int)
3840

3941
T = TypeVar('T')
4042
M = TypeVar('M', bound=Mapping)
@@ -182,53 +184,6 @@ def _re_sub(match: re.Match[str]) -> str:
182184
return RE_JSONC.sub(_re_sub, text,)
183185

184186

185-
# FIXME: Move to generic utils/funcs?
186-
def exc_amend(exc: Exception, text: str) -> Exception:
187-
""" Helper to prefix text to an exception """
188-
args = list(exc.args)
189-
if len(args) > 0:
190-
args[0] = text + str(args[0])
191-
else:
192-
args.append(text)
193-
exc.args = tuple(args)
194-
return exc
195-
196-
197-
def str_to_int(string: str|int) -> int:
198-
""" Convert string or int to int. Fail if not possible."""
199-
i = maybe_number(string)
200-
if not isinstance(i, int):
201-
raise ValueError(f"Expected integer, got '{string}'")
202-
return i
203-
204-
205-
def maybe_number(string: str|int) -> int|str:
206-
""" Convert string to a number, otherwise pass it through as-is"""
207-
if isinstance(string, int):
208-
return string
209-
s = string.strip()
210-
if s.startswith('0x') or s.startswith('-0x'):
211-
return int(s.replace('0x', ''), 16)
212-
if s.isdigit():
213-
return int(string)
214-
return string
215-
216-
217-
def copy_in_order(d: M, order: Sequence[T]) -> M:
218-
""" Remake dict d with keys in order """
219-
out = {
220-
k: d[k]
221-
for k in order
222-
if k in d
223-
}
224-
out.update({
225-
k: v
226-
for k, v in d.items()
227-
if k not in out
228-
})
229-
return cast(M, out) # FIXME: For mypy
230-
231-
232187
def remove_underscore(d: T) -> T:
233188
""" Recursively remove any keys prefixed with '__' """
234189
if isinstance(d, dict):

src/objdictgen/node.py

-197
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
from pathlib import Path
2424
from typing import Any, Generator, Iterable, Iterator
2525

26-
import colorama
27-
2826
# The following import needs care when importing node
2927
from objdictgen import eds_utils, gen_cfile, jsonod, maps, nosis
3028
from objdictgen.maps import OD, ODMapping, ODMappingList
@@ -33,9 +31,6 @@
3331

3432
log = logging.getLogger('objdictgen')
3533

36-
Fore = colorama.Fore
37-
Style = colorama.Style
38-
3934

4035
# ------------------------------------------------------------------------------
4136
# Definition of Node Object
@@ -1000,198 +995,6 @@ def _warn(text: str):
1000995
subvals["name"] = f"Subindex {idx}"
1001996
_warn(f"FIX: Set name to '{subvals['name']}'")
1002997

1003-
# --------------------------------------------------------------------------
1004-
# Printing and output
1005-
# --------------------------------------------------------------------------
1006-
1007-
def GetPrintEntryHeader(
1008-
self, index: int, unused=False, compact=False, raw=False,
1009-
entry: TIndexEntry|None = None
1010-
) -> tuple[str, dict[str, str]]:
1011-
1012-
# Get the information about the index if it wasn't passed along
1013-
if not entry:
1014-
entry = self.GetIndexEntry(index, withbase=True)
1015-
obj = entry["object"]
1016-
1017-
# Get the flags for the entry
1018-
flags: set[str] = set()
1019-
for group in entry["groups"]:
1020-
v = {
1021-
"built-in": None,
1022-
"user": "User",
1023-
"ds302": "DS-302",
1024-
"profile": "Profile",
1025-
}.get(group, group)
1026-
if v:
1027-
flags.add(v)
1028-
if obj.get('need'):
1029-
flags.add("Mandatory")
1030-
if entry.get("params", {}).get("callback"):
1031-
flags.add('CB')
1032-
if "dictionary" not in entry:
1033-
if "ds302" in entry["groups"] or "profile" in entry["groups"]:
1034-
flags.add("Unused")
1035-
else:
1036-
flags.add("Missing")
1037-
1038-
# Skip printing if the entry is unused and we are not printing unused
1039-
if 'Unused' in flags and not unused:
1040-
return '', {}
1041-
1042-
# Replace flags for formatting
1043-
for _, flag in enumerate(flags.copy()):
1044-
if flag == 'Missing':
1045-
flags.discard('Missing')
1046-
flags.add(Fore.RED + ' *MISSING* ' + Style.RESET_ALL)
1047-
1048-
# Print formattings
1049-
idx = (index - entry.get("base", index)) // obj.get("incr", 1) + 1
1050-
t_name = obj['name']
1051-
if not raw:
1052-
t_name = maps.eval_name(t_name, idx=idx, sub=0)
1053-
t_flags = ', '.join(flags)
1054-
t_string = maps.ODStructTypes.to_string(obj['struct']) or '???'
1055-
1056-
# ** PRINT PARAMETER **
1057-
return "{pre}{key} {name} {struct}{flags}", {
1058-
'key': f"{Fore.LIGHTGREEN_EX}0x{index:04x} ({index}){Style.RESET_ALL}",
1059-
'name': f"{Fore.LIGHTWHITE_EX}{t_name}{Style.RESET_ALL}",
1060-
'struct': f"{Fore.LIGHTYELLOW_EX}[{t_string.upper()}]{Style.RESET_ALL}",
1061-
'flags': f" {Fore.MAGENTA}{t_flags}{Style.RESET_ALL}" if flags else '',
1062-
'pre': ' ' if not compact else '',
1063-
}
1064-
1065-
def GetPrintEntry(
1066-
self, keys: list[int]|None = None, short=False, compact=False,
1067-
unused=False, verbose=False, raw=False,
1068-
) -> Generator[str, None, None]:
1069-
"""
1070-
Generator for printing the dictionary values
1071-
"""
1072-
1073-
# Get the indexes to print and determine the order
1074-
keys = keys or self.GetAllIndices(sort=True)
1075-
1076-
index_range = None
1077-
for k in keys:
1078-
1079-
# Get the index entry information
1080-
param = self.GetIndexEntry(k, withbase=True)
1081-
obj = param["object"]
1082-
1083-
# Get the header for the entry
1084-
line, fmt = self.GetPrintEntryHeader(
1085-
k, unused=unused, compact=compact, entry=param, raw=raw
1086-
)
1087-
if not line:
1088-
continue
1089-
1090-
# Print the parameter range header
1091-
ir = maps.INDEX_RANGES.get_index_range(k)
1092-
if index_range != ir:
1093-
index_range = ir
1094-
if not compact:
1095-
yield Fore.YELLOW + ir.description + Style.RESET_ALL
1096-
1097-
# Yield the parameter header
1098-
yield line.format(**fmt)
1099-
1100-
# Omit printing sub index data if:
1101-
if short:
1102-
continue
1103-
1104-
# Fetch the dictionary values and the parameters, if present
1105-
if k in self.Dictionary:
1106-
values = self.GetEntry(k, aslist=True, compute=not raw)
1107-
else:
1108-
values = ['__N/A__'] * len(obj["values"])
1109-
if k in self.ParamsDictionary:
1110-
params = self.GetParamsEntry(k, aslist=True)
1111-
else:
1112-
params = [maps.DEFAULT_PARAMS] * len(obj["values"])
1113-
# For mypy to ensure that values and entries are lists
1114-
assert isinstance(values, list) and isinstance(params, list)
1115-
1116-
infos = []
1117-
for i, (value, param) in enumerate(zip(values, params)):
1118-
1119-
# Prepare data for printing
1120-
info = self.GetSubentryInfos(k, i)
1121-
typename = self.GetTypeName(info['type'])
1122-
1123-
# Type specific formatting of the value
1124-
if value == "__N/A__":
1125-
t_value = f'{Fore.LIGHTBLACK_EX}N/A{Style.RESET_ALL}'
1126-
elif isinstance(value, str):
1127-
length = len(value)
1128-
if typename == 'DOMAIN':
1129-
value = value.encode('unicode_escape').decode()
1130-
t_value = '"' + value + f'" ({length})'
1131-
elif i and index_range and index_range.name in ('rpdom', 'tpdom'):
1132-
# FIXME: In PDO mappings, the value is ints
1133-
assert isinstance(value, int)
1134-
index, subindex, _ = self.GetMapIndex(value)
1135-
try:
1136-
pdo = self.GetSubentryInfos(index, subindex)
1137-
t_v = f"{value:x}"
1138-
t_value = f"0x{t_v[0:4]}_{t_v[4:6]}_{t_v[6:]} {Fore.LIGHTCYAN_EX}{pdo['name']}{Style.RESET_ALL}"
1139-
except ValueError:
1140-
suffix = ' ???' if value else ''
1141-
t_value = f"0x{value:x}{suffix}"
1142-
elif i and value and (k in (4120, ) or 'COB ID' in info["name"]):
1143-
t_value = f"0x{value:x}"
1144-
else:
1145-
t_value = str(value)
1146-
1147-
# Add comment if present
1148-
t_comment = param['comment'] or ''
1149-
if t_comment:
1150-
t_comment = f"{Fore.LIGHTBLACK_EX}/* {t_comment} */{Style.RESET_ALL}"
1151-
1152-
# Omit printing the first element unless specifically requested
1153-
if (not verbose and i == 0
1154-
and obj['struct'] & OD.MultipleSubindexes
1155-
and not t_comment
1156-
):
1157-
continue
1158-
1159-
# Print formatting
1160-
infos.append({
1161-
'i': f"{Fore.GREEN}{i:02d}{Style.RESET_ALL}",
1162-
'access': info['access'],
1163-
'pdo': 'P' if info['pdo'] else ' ',
1164-
'name': info['name'],
1165-
'type': f"{Fore.LIGHTBLUE_EX}{typename}{Style.RESET_ALL}",
1166-
'value': t_value,
1167-
'comment': t_comment,
1168-
'pre': fmt['pre'],
1169-
})
1170-
1171-
# Must skip the next step if list is empty, as the first element is
1172-
# used for the header
1173-
if not infos:
1174-
continue
1175-
1176-
# Calculate the max width for each of the columns
1177-
w = {
1178-
col: max(len(str(row[col])) for row in infos) or ''
1179-
for col in infos[0]
1180-
}
1181-
1182-
# Generate a format string based on the calculcated column widths
1183-
# Legitimate use of % as this is making a string containing format specifiers
1184-
fmt = "{pre} {i:%ss} {access:%ss} {pdo:%ss} {name:%ss} {type:%ss} {value:%ss} {comment}" % (
1185-
w["i"], w["access"], w["pdo"], w["name"], w["type"], w["value"]
1186-
)
1187-
1188-
# Print each line using the generated format string
1189-
for infoentry in infos:
1190-
yield fmt.format(**infoentry)
1191-
1192-
if not compact and infos:
1193-
yield ""
1194-
1195998

1196999
# Register node with gnosis
11971000
nosis.add_class_to_store('Node', Node)

src/objdictgen/nodelist.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from objdictgen import eds_utils
2828
from objdictgen.node import Node
2929
from objdictgen.nodemanager import NodeManager
30+
from objdictgen.printing import GetPrintEntry
3031
from objdictgen.typing import TODObj, TODSubObj, TPath
3132

3233
# ------------------------------------------------------------------------------
@@ -269,11 +270,11 @@ def main(projectdir):
269270
print("MasterNode :")
270271
node = manager.CurrentNode
271272
if node:
272-
for line in node.GetPrintEntry(raw=True):
273+
for line in GetPrintEntry(node, raw=True):
273274
print(line)
274275
print()
275276
for nodeid, nodeinfo in nodelist.SlaveNodes.items():
276277
print(f"SlaveNode name={nodeinfo.Name} id=0x{nodeid:02X} :")
277-
for line in nodeinfo.Node.GetPrintEntry():
278+
for line in GetPrintEntry(nodeinfo.Node):
278279
print(line)
279280
print()

0 commit comments

Comments
 (0)