Source code for mmodel.node

from mmodel.signature import (
    modify_signature,
    add_signature,
    convert_signature,
    has_signature,
    check_signature,
)
from inspect import signature
from mmodel.metadata import nodeformatter
from mmodel.utility import construction_dict, modify_func, parse_functype
from mmodel.model import Model


[docs]class Node: """A node class that formats node function and metadata.""" def __init__(self, name, func, inputs=None, output=None, modifiers=None, **kwargs): # static self.name = self.__name__ = name self.output = output self._inputs = inputs or [] self._modifiers = modifiers or [] self.func = func self.functype = parse_functype(func) self.doc = func.__doc__ self._base_func = self.convert_func(func, self._inputs) # allow overwrite self.node_func = modify_func(self._base_func, self._modifiers) # kwargs can overwrite values like doc, functype, etc. for key, value in kwargs.items(): setattr(self, key, value) @property def __signature__(self): """Node signature for inspection.""" return signature(self.node_func) @property def signature(self): """Return signature.""" return self.__signature__
[docs] def convert_func(self, func, inputs): """Convert function to a node function. For ``numpy.ufunc`` and builtin type, the "inputs" argument is required. If inputs are provided, the signature is changed based on the inputs and keyword-only. If the "inputs" is None, the signature is changed keyword only, and the default values are removed. The keyword-only design reduced binding overhead during the function calls, and allow more consistency between node, modifier, and model. """ if isinstance(func, Model): func = func.model_func if not has_signature(func): if not inputs: raise Exception(f"'inputs' required for node {repr(self.name)}") else: func = add_signature(func, inputs) elif inputs: func = modify_signature(func, inputs) elif not check_signature(func): func = convert_signature(func) return func
@property def inputs(self): """Return a copy of inputs.""" return self._inputs.copy() @property def modifiers(self): """Return a copy of modifiers.""" return self._modifiers.copy()
[docs] def edit(self, **kwargs): """Edit node. A new node object is created.""" con_dict = construction_dict( self, ["inputs", "modifiers"], ["functype", "doc", "node_func"] ) con_dict.update(kwargs) return self.__class__(**con_dict)
def __call__(self, *args, **kwargs): """Node function callable. The ``node_func`` method is used internally. The ``__call__`` method is used for external calls. """ bound = self.signature.bind(*args, **kwargs) bound.apply_defaults() # There's no defaults allowed, added regardless return self.node_func(**bound.arguments) def __str__(self): return nodeformatter(self)