diff --git a/ait/core/cfg.py b/ait/core/cfg.py index ec95babd..126c9103 100644 --- a/ait/core/cfg.py +++ b/ait/core/cfg.py @@ -43,7 +43,8 @@ def expand_config_paths( config, prefix=None, datetime=None, pathvars=None, parameter_key="", *keys ): - """Updates all relative configuration paths in dictionary config, + """ + Updates all relative configuration paths in dictionary config, which contain a key in keys, by prepending prefix. If keys is omitted, it defaults to 'directory', 'file', @@ -377,7 +378,6 @@ def reload(self, filename=None, data=None): self._datetime, merge(self._config, self._pathvars), ) - else: self._config = {} diff --git a/ait/core/cmd.py b/ait/core/cmd.py index 144c59cb..471d96f7 100644 --- a/ait/core/cmd.py +++ b/ait/core/cmd.py @@ -17,6 +17,7 @@ The ait.core.cmd module provides commands and command dictionaries. Dictionaries contain command and argument definitions. + """ import os @@ -43,6 +44,7 @@ class ArgDefn(json.SlotSerializer, object): A fixed argument (fixed=True) defines a fixed bit pattern in that argument's byte position(s). + """ __slots__ = [ @@ -104,8 +106,10 @@ def startbit(self): return self.slice().start % 2 * 8 def decode(self, bytes): - """Decodes the given bytes according to this AIT Argument + """ + Decodes the given bytes according to this AIT Argument Definition. + """ value = self.type.decode(bytes) if self._enum is not None: @@ -116,8 +120,10 @@ def decode(self, bytes): return value def encode(self, value): - """Encodes the given value according to this AIT Argument + """ + Encodes the given value according to this AIT Argument Definition. + """ if not self.type: return bytearray() @@ -128,10 +134,12 @@ def encode(self, value): def slice(self, offset=0): - """Returns a Python slice object (e.g. for array indexing) indicating + """ + Returns a Python slice object (e.g. for array indexing) indicating the start and stop byte position of this Command argument. The start and stop positions may be translated by the optional byte offset. + """ if type(self.bytes) is int: start = self.bytes @@ -143,9 +151,11 @@ def slice(self, offset=0): return slice(start + offset, stop + offset) def validate(self, value, messages=None): - """Returns True if the given Argument value is valid, False otherwise. + """ + Returns True if the given Argument value is valid, False otherwise. Validation error messages are appended to an optional messages array. + """ valid = True primitive = value @@ -176,15 +186,19 @@ def log(msg): class Cmd(object): - """Cmd - Command + """ + Cmd - Command Commands reference their Command Definition and may contain arguments. + """ def __init__(self, defn, *args, **kwargs): - """Creates a new AIT Command based on the given command + """ + Creates a new AIT Command based on the given command definition and command arguments. A Command may be created with either positional or keyword arguments, but not both. + """ self.defn = defn @@ -234,7 +248,8 @@ def argdefns(self): return self.defn.argdefns def encode(self, pad=106): - """Encodes this AIT command to binary. + """ + Encodes this AIT command to binary. If pad is specified, it indicates the maximum size of the encoded command in bytes. If the encoded command is less than pad, the @@ -244,6 +259,7 @@ def encode(self, pad=106): (128 bytes) with 11 words (22 bytes) of CCSDS overhead (SSP 52050J, Section 3.2.3.4). This leaves 53 words (106 bytes) for the command itself. + """ try: opcode = struct.pack(">H", self.defn.opcode) @@ -272,20 +288,24 @@ def encode(self, pad=106): return encoded def validate(self, messages=None): - """Returns True if the given Command is valid, False otherwise. + """ + Returns True if the given Command is valid, False otherwise. Validation error messages are appended to an optional messages array. + """ return self.defn.validate(self, messages) class CmdDefn(json.SlotSerializer, object): - """CmdDefn - Command Definition + """ + mdDefn - Command Definition Command Definitions encapsulate all information required to define a single command. This includes the command name, its opcode, subsystem, description and a list of argument definitions. Name and opcode are required. All others are optional. + """ __slots__ = ("name", "_opcode", "subsystem", "ccsds", "title", "desc", "argdefns") @@ -308,27 +328,31 @@ def __repr__(self): return util.toRepr(self) @property - def args(self): - """The argument definitions to this command (excludes fixed + def args (self): + """ + The argument definitions to this command (excludes fixed arguments). """ return filter(lambda a: not a.fixed, self.argdefns) @property def nargs(self): - """The number of arguments to this command (excludes fixed + """ + The number of arguments to this command (excludes fixed arguments). """ return len(list(self.args)) @property def nbytes(self): - """The number of bytes required to encode this command. + """ + The number of bytes required to encode this command. Encoded commands are comprised of a two byte opcode, followed by a one byte size, and then the command argument bytes. The size indicates the number of bytes required to represent command arguments. + """ return len(self.opcode) + 1 + sum(arg.nbytes for arg in self.argdefns) @@ -449,8 +473,10 @@ def create(self, name, *args, **kwargs): return createCmd(defn, *args, **kwargs) # noqa + def decode(self, bytes): - """Decodes the given bytes according to this AIT Command + """ + Decodes the given bytes according to this AIT Command Definition. """ opcode = struct.unpack(">H", bytes[0:2])[0] @@ -499,7 +525,7 @@ def toJSON(self): # noqa return {name: defn.toJSON() for name, defn in self.items()} -def getDefaultCmdDict(reload=False): # noqa +def getDefaultCmdDict(reload=False): return getDefaultDict(reload=reload) @@ -554,6 +580,15 @@ def YAMLCtor_CmdDefn(loader, node): # noqa return createCmdDefn(**fields) # noqa +def YAMLCtor_include(loader, node): + # Get the path out of the yaml file + name = os.path.join(os.path.dirname(loader.name), node.value) + data = None + with open(name,'r') as f: + data = yaml.load(f) + return data + + def YAMLCtor_include(loader, node): # noqa # Get the path out of the yaml file name = os.path.join(os.path.dirname(loader.name), node.value) diff --git a/ait/core/dtype.py b/ait/core/dtype.py index 3aff1281..841662e5 100644 --- a/ait/core/dtype.py +++ b/ait/core/dtype.py @@ -74,7 +74,6 @@ from ait.core import cmd, dmc, log - # PrimitiveTypes # # Lists PrimitiveType names. Passing these names to get() will return diff --git a/ait/core/table.py b/ait/core/table.py index 857456b6..735aef2a 100644 --- a/ait/core/table.py +++ b/ait/core/table.py @@ -11,6 +11,7 @@ # laws and regulations. User has the responsibility to obtain export licenses, # or other export authority as may be required before exporting such # information to foreign countries or providing access to foreign persons. + import datetime import hashlib import io @@ -205,6 +206,7 @@ class FSWTabDefn(object): single column. This includes the column name, its opcode, subsystem, description and a list of argument definitions. Name and opcode are required. All others are optional. + """ def __init__(self, *args, **kwargs): @@ -375,6 +377,7 @@ def _decode_table_row(self, in_stream, raw=False): Raises: ValueError: When an EOFError is encountered while decoding any column but the first or if any column decode returns None. + """ row = [] @@ -406,6 +409,7 @@ class FSWTabDict(dict): Table Dictionaries provide a Python dictionary (i.e. hashtable) interface mapping Tables names to Column Definitions. + """ def __init__(self, *args, **kwargs): @@ -429,7 +433,7 @@ def create(self, name, *args): tab = None defn = self.get(name, None) if defn: - tab = FSWTab(defn, *args) + tab = createFSWTab(defn, *args) return tab def load(self, filename): @@ -463,7 +467,7 @@ def dirty(self): def load(self): if self.fswtabdict is None: if self.dirty(): - self.fswtabdict = FSWTabDict(self.filename) + self.fswtabdict = createFSWTabDict(self.filename) self.update() else: with open(self.pcklname, "rb") as stream: @@ -499,14 +503,14 @@ def getDefaultDict(): # noqa: N802 def YAMLCtor_FSWColDefn(loader, node): # noqa: N802 fields = loader.construct_mapping(node, deep=True) - return FSWColDefn(**fields) + return createFSWColDefn(**fields) def YAMLCtor_FSWTabDefn(loader, node): # noqa: N802 fields = loader.construct_mapping(node, deep=True) fields["fswheaderdefns"] = fields.pop("header", None) fields["coldefns"] = fields.pop("columns", None) - return FSWTabDefn(**fields) + return createFSWTabDefn(**fields) def encode_to_file(tbl_type, in_path, out_path): @@ -544,3 +548,5 @@ def decode_to_file(tbl_type, in_path, out_path): yaml.add_constructor("!FSWTable", YAMLCtor_FSWTabDefn) yaml.add_constructor("!FSWColumn", YAMLCtor_FSWColDefn) + +util.__init_extensions__(__name__, globals()) diff --git a/ait/core/util.py b/ait/core/util.py index 6464b4bf..2c26ede5 100755 --- a/ait/core/util.py +++ b/ait/core/util.py @@ -120,10 +120,12 @@ def check_yaml_timestamps(self, yaml_file_name, cache_name): return False def load(self): - """Loads the Python object + """ + Loads the Python object Loads the Python object, either via loader(filename) or the pickled cache file, whichever was modified most recently. + """ if self._dict is None: @@ -165,6 +167,7 @@ def __init_extensions__(modname, modsyms): # noqa :func:`createCmd()`) it will now create a ``FooCmd`` object instead. Note: ``FooCmd`` should adhere to the same interface as :class:`ait.core.cmd.Cmd` (and probably inherit from it). + """ def createFunc(cls, extname): # noqa @@ -176,6 +179,7 @@ class name (*extname*). returned ``createXXX()`` is called, it attempts to lookup and load the class. Thereafter, the loaded class is cached for subsequent calls. + """ def create(*args, **kwargs): @@ -187,6 +191,7 @@ def create(*args, **kwargs): if module is None: raise ImportError("No module named %d" % modname) create.cls = getattr(module, clsname) + if create.cls is None: raise ImportError("No class named %s" % extname) return create.cls(*args, **kwargs) @@ -197,11 +202,11 @@ def create(*args, **kwargs): extensions = ait.config.get("extensions", None) for clsname, cls in modsyms.copy().items(): + if not isinstance(cls, type): continue extname = None - if extensions: extname = extensions.get(modname + "." + clsname, None) @@ -217,12 +222,12 @@ def __load_functions__(symtbl): # noqa """Loads all Python functions from the module specified in the ``functions`` configuration parameter (in config.yaml) into the given symbol table (Python dictionary). + """ modname = ait.config.get("functions", None) if modname: module = pydoc.locate(modname) - if module is None: msg = "No module named %d (from config.yaml functions: parameter)" raise ImportError(msg % modname) @@ -236,6 +241,7 @@ def __load_functions__(symtbl): # noqa def crc32File(filename, skip=0): # noqa """Computes the CRC-32 of the contents of filename, optionally skipping a certain number of bytes at the beginning of the file. + """ with open(filename, "rb") as stream: _ = stream.read(skip) @@ -264,14 +270,15 @@ def setDictDefaults(d, defaults): # noqa def getDefaultDict(modname, config_key, loader, reload=False, filename=None): # noqa - """Returns default AIT dictonary for modname + """Returns default AIT dictionary for modname - This helper function encapulates the core logic necessary to + This helper function encapsulates the core logic necessary to (re)load, cache (via util.ObjectCache), and return the default dictionary. For example, in ait.core.cmd: def getDefaultDict(reload=False): return ait.util.getDefaultDict(__name__, 'cmddict', CmdDict, reload) + """ module = sys.modules[modname] default = getattr(module, "DefaultDict", None) @@ -331,6 +338,7 @@ def toFloat(str, default=None): # noqa >>> f = toFloat("Foo") >>> assert f is None """ + value = default try: @@ -365,6 +373,7 @@ def toNumber(str, default=None): # noqa >>> n = toNumber("Foo") >>> assert n is None """ + value = default try: @@ -386,6 +395,7 @@ def toNumberOrStr(str): # noqa Converts the given string to a numeric value, if possible. Otherwise returns the input string + """ return toNumber(str, str) @@ -437,6 +447,7 @@ def expandPath(pathname, prefix=None): # noqa """Return pathname as an absolute path, either expanded by the users home directory ("~") or with prefix prepended. """ + if prefix is None: prefix = "" @@ -454,6 +465,7 @@ def listAllFiles(directory, suffix=None, abspath=False): # noqa """Returns the list of all files within the input directory and all subdirectories. """ + files = [] directory = expandPath(directory)