Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move functions to separate concerns #53

Merged
merged 1 commit into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/objdictgen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
from objdictgen.node import Node
from objdictgen.nodemanager import NodeManager

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

# Shortcuts
Expand Down
5 changes: 3 additions & 2 deletions src/objdictgen/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import objdictgen
from objdictgen import jsonod
from objdictgen.printing import GetPrintEntry
from objdictgen.typing import TDiffEntries, TDiffNodes, TPath

T = TypeVar('T')
Expand Down Expand Up @@ -168,8 +169,8 @@ def list_od(
yield ""

# Print the parameters
yield from od.GetPrintEntry(
keys=keys, short=opts.short, compact=opts.compact, unused=opts.unused,
yield from GetPrintEntry(
od, keys=keys, short=opts.short, compact=opts.compact, unused=opts.unused,
verbose=opts.all, raw=opts.raw
)

Expand Down
49 changes: 2 additions & 47 deletions src/objdictgen/jsonod.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
from objdictgen.maps import OD, ODMapping, ODMappingList
from objdictgen.typing import (TDiffNodes, TIndexEntry, TODJson, TODObjJson,
TODObj, TODSubObj, TODSubObjJson, TODValue, TParamEntry, TPath, TProfileMenu)
from objdictgen.utils import (copy_in_order, exc_amend, maybe_number,
str_to_int)

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


# FIXME: Move to generic utils/funcs?
def exc_amend(exc: Exception, text: str) -> Exception:
""" Helper to prefix text to an exception """
args = list(exc.args)
if len(args) > 0:
args[0] = text + str(args[0])
else:
args.append(text)
exc.args = tuple(args)
return exc


def str_to_int(string: str|int) -> int:
""" Convert string or int to int. Fail if not possible."""
i = maybe_number(string)
if not isinstance(i, int):
raise ValueError(f"Expected integer, got '{string}'")
return i


def maybe_number(string: str|int) -> int|str:
""" Convert string to a number, otherwise pass it through as-is"""
if isinstance(string, int):
return string
s = string.strip()
if s.startswith('0x') or s.startswith('-0x'):
return int(s.replace('0x', ''), 16)
if s.isdigit():
return int(string)
return string


def copy_in_order(d: M, order: Sequence[T]) -> M:
""" Remake dict d with keys in order """
out = {
k: d[k]
for k in order
if k in d
}
out.update({
k: v
for k, v in d.items()
if k not in out
})
return cast(M, out) # FIXME: For mypy


def remove_underscore(d: T) -> T:
""" Recursively remove any keys prefixed with '__' """
if isinstance(d, dict):
Expand Down
197 changes: 0 additions & 197 deletions src/objdictgen/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
from pathlib import Path
from typing import Any, Generator, Iterable, Iterator

import colorama

# The following import needs care when importing node
from objdictgen import eds_utils, gen_cfile, jsonod, maps, nosis
from objdictgen.maps import OD, ODMapping, ODMappingList
Expand All @@ -33,9 +31,6 @@

log = logging.getLogger('objdictgen')

Fore = colorama.Fore
Style = colorama.Style


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

# --------------------------------------------------------------------------
# Printing and output
# --------------------------------------------------------------------------

def GetPrintEntryHeader(
self, index: int, unused=False, compact=False, raw=False,
entry: TIndexEntry|None = None
) -> tuple[str, dict[str, str]]:

# Get the information about the index if it wasn't passed along
if not entry:
entry = self.GetIndexEntry(index, withbase=True)
obj = entry["object"]

# Get the flags for the entry
flags: set[str] = set()
for group in entry["groups"]:
v = {
"built-in": None,
"user": "User",
"ds302": "DS-302",
"profile": "Profile",
}.get(group, group)
if v:
flags.add(v)
if obj.get('need'):
flags.add("Mandatory")
if entry.get("params", {}).get("callback"):
flags.add('CB')
if "dictionary" not in entry:
if "ds302" in entry["groups"] or "profile" in entry["groups"]:
flags.add("Unused")
else:
flags.add("Missing")

# Skip printing if the entry is unused and we are not printing unused
if 'Unused' in flags and not unused:
return '', {}

# Replace flags for formatting
for _, flag in enumerate(flags.copy()):
if flag == 'Missing':
flags.discard('Missing')
flags.add(Fore.RED + ' *MISSING* ' + Style.RESET_ALL)

# Print formattings
idx = (index - entry.get("base", index)) // obj.get("incr", 1) + 1
t_name = obj['name']
if not raw:
t_name = maps.eval_name(t_name, idx=idx, sub=0)
t_flags = ', '.join(flags)
t_string = maps.ODStructTypes.to_string(obj['struct']) or '???'

# ** PRINT PARAMETER **
return "{pre}{key} {name} {struct}{flags}", {
'key': f"{Fore.LIGHTGREEN_EX}0x{index:04x} ({index}){Style.RESET_ALL}",
'name': f"{Fore.LIGHTWHITE_EX}{t_name}{Style.RESET_ALL}",
'struct': f"{Fore.LIGHTYELLOW_EX}[{t_string.upper()}]{Style.RESET_ALL}",
'flags': f" {Fore.MAGENTA}{t_flags}{Style.RESET_ALL}" if flags else '',
'pre': ' ' if not compact else '',
}

def GetPrintEntry(
self, keys: list[int]|None = None, short=False, compact=False,
unused=False, verbose=False, raw=False,
) -> Generator[str, None, None]:
"""
Generator for printing the dictionary values
"""

# Get the indexes to print and determine the order
keys = keys or self.GetAllIndices(sort=True)

index_range = None
for k in keys:

# Get the index entry information
param = self.GetIndexEntry(k, withbase=True)
obj = param["object"]

# Get the header for the entry
line, fmt = self.GetPrintEntryHeader(
k, unused=unused, compact=compact, entry=param, raw=raw
)
if not line:
continue

# Print the parameter range header
ir = maps.INDEX_RANGES.get_index_range(k)
if index_range != ir:
index_range = ir
if not compact:
yield Fore.YELLOW + ir.description + Style.RESET_ALL

# Yield the parameter header
yield line.format(**fmt)

# Omit printing sub index data if:
if short:
continue

# Fetch the dictionary values and the parameters, if present
if k in self.Dictionary:
values = self.GetEntry(k, aslist=True, compute=not raw)
else:
values = ['__N/A__'] * len(obj["values"])
if k in self.ParamsDictionary:
params = self.GetParamsEntry(k, aslist=True)
else:
params = [maps.DEFAULT_PARAMS] * len(obj["values"])
# For mypy to ensure that values and entries are lists
assert isinstance(values, list) and isinstance(params, list)

infos = []
for i, (value, param) in enumerate(zip(values, params)):

# Prepare data for printing
info = self.GetSubentryInfos(k, i)
typename = self.GetTypeName(info['type'])

# Type specific formatting of the value
if value == "__N/A__":
t_value = f'{Fore.LIGHTBLACK_EX}N/A{Style.RESET_ALL}'
elif isinstance(value, str):
length = len(value)
if typename == 'DOMAIN':
value = value.encode('unicode_escape').decode()
t_value = '"' + value + f'" ({length})'
elif i and index_range and index_range.name in ('rpdom', 'tpdom'):
# FIXME: In PDO mappings, the value is ints
assert isinstance(value, int)
index, subindex, _ = self.GetMapIndex(value)
try:
pdo = self.GetSubentryInfos(index, subindex)
t_v = f"{value:x}"
t_value = f"0x{t_v[0:4]}_{t_v[4:6]}_{t_v[6:]} {Fore.LIGHTCYAN_EX}{pdo['name']}{Style.RESET_ALL}"
except ValueError:
suffix = ' ???' if value else ''
t_value = f"0x{value:x}{suffix}"
elif i and value and (k in (4120, ) or 'COB ID' in info["name"]):
t_value = f"0x{value:x}"
else:
t_value = str(value)

# Add comment if present
t_comment = param['comment'] or ''
if t_comment:
t_comment = f"{Fore.LIGHTBLACK_EX}/* {t_comment} */{Style.RESET_ALL}"

# Omit printing the first element unless specifically requested
if (not verbose and i == 0
and obj['struct'] & OD.MultipleSubindexes
and not t_comment
):
continue

# Print formatting
infos.append({
'i': f"{Fore.GREEN}{i:02d}{Style.RESET_ALL}",
'access': info['access'],
'pdo': 'P' if info['pdo'] else ' ',
'name': info['name'],
'type': f"{Fore.LIGHTBLUE_EX}{typename}{Style.RESET_ALL}",
'value': t_value,
'comment': t_comment,
'pre': fmt['pre'],
})

# Must skip the next step if list is empty, as the first element is
# used for the header
if not infos:
continue

# Calculate the max width for each of the columns
w = {
col: max(len(str(row[col])) for row in infos) or ''
for col in infos[0]
}

# Generate a format string based on the calculcated column widths
# Legitimate use of % as this is making a string containing format specifiers
fmt = "{pre} {i:%ss} {access:%ss} {pdo:%ss} {name:%ss} {type:%ss} {value:%ss} {comment}" % (
w["i"], w["access"], w["pdo"], w["name"], w["type"], w["value"]
)

# Print each line using the generated format string
for infoentry in infos:
yield fmt.format(**infoentry)

if not compact and infos:
yield ""


# Register node with gnosis
nosis.add_class_to_store('Node', Node)
5 changes: 3 additions & 2 deletions src/objdictgen/nodelist.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from objdictgen import eds_utils
from objdictgen.node import Node
from objdictgen.nodemanager import NodeManager
from objdictgen.printing import GetPrintEntry
from objdictgen.typing import TODObj, TODSubObj, TPath

# ------------------------------------------------------------------------------
Expand Down Expand Up @@ -269,11 +270,11 @@ def main(projectdir):
print("MasterNode :")
node = manager.CurrentNode
if node:
for line in node.GetPrintEntry(raw=True):
for line in GetPrintEntry(node, raw=True):
print(line)
print()
for nodeid, nodeinfo in nodelist.SlaveNodes.items():
print(f"SlaveNode name={nodeinfo.Name} id=0x{nodeid:02X} :")
for line in nodeinfo.Node.GetPrintEntry():
for line in GetPrintEntry(nodeinfo.Node):
print(line)
print()
Loading