Beispiel #1
0
    def __init__(self,
                 parent,
                 name,
                 formula=None,
                 refs=None,
                 source=None,
                 doc=None):
        BaseSpaceImpl.__init__(self,
                               parent=parent,
                               name=name,
                               formula=formula,
                               refs=refs,
                               source=source,
                               doc=doc)

        self._refs = ImplChainMap(
            self,
            RefView,
            [self.model._global_refs, self._local_refs, self._self_refs],
        )

        self._namespace_impl = ImplChainMap(
            self, None, [self._cells, self._refs, self._spaces])

        self.lazy_evals = self._namespace_impl
Beispiel #2
0
    def __init__(self, *, system, name):
        Impl.__init__(self, system=system)
        EditableSpaceContainerImpl.__init__(self)

        self.cellgraph = DependencyGraph()
        self.lexdep = DependencyGraph()  # Lexical dependency
        self.spacegraph = SpaceGraph()
        self.currentspace = None

        if not name:
            self.name = system._modelnamer.get_next(system.models)
        elif is_valid_name(name):
            self.name = name
        else:
            raise ValueError("Invalid name '%s'." % name)

        data = {"__builtins__": builtins}
        self._global_refs = RefDict(self, data=data)
        self._spaces = ImplDict(self, SpaceView)
        self._dynamic_bases = {}
        self._dynamic_bases_inverse = {}
        self._dynamic_base_namer = AutoNamer("__Space")
        self._namespace = ImplChainMap(self, BaseView,
                                       [self._spaces, self._global_refs])
        self.allow_none = False
        self.lazy_evals = self._namespace
Beispiel #3
0
    def __init__(self, *, system, name):

        if not name:
            name = system._modelnamer.get_next(system.models)
        elif not is_valid_name(name):
            raise ValueError("Invalid name '%s'." % name)

        Impl.__init__(self, system=system, parent=None, name=name)
        EditableSpaceContainerImpl.__init__(self)
        TraceManager.__init__(self)

        self.spacemgr = SpaceManager(self)
        self.currentspace = None
        self._global_refs = RefDict(self)
        self._global_refs.set_item("__builtins__", builtins)
        self._named_spaces = SpaceDict(self)
        self._dynamic_bases = SpaceDict(self)
        self._all_spaces = ImplChainMap(
            self, SpaceView, [self._named_spaces, self._dynamic_bases])
        self._dynamic_bases_inverse = {}
        self._dynamic_base_namer = AutoNamer("__Space")
        self._namespace = ImplChainMap(self, BaseView,
                                       [self._named_spaces, self._global_refs])
        self.allow_none = False
        self.lazy_evals = self._namespace
        self.datarefmgr = DataClientReferenceManager()
Beispiel #4
0
    def _create_parentargs(self):
        if isinstance(self.parent, StaticSpaceImpl):
            parentargs = []
        elif isinstance(self.parent, RootDynamicSpaceImpl):
            parentargs = [self.parent._arguments, self.parent._parentargs]
        else:
            parentargs = [self.parent._parentargs]

        return ImplChainMap(self, None, parentargs)
Beispiel #5
0
    def _create_refs(self, arguments=None):
        self._parentargs = self._create_parentargs()

        return ImplChainMap(
            self,
            RefView,
            [
                self.model._global_refs,
                self._local_refs,
                self._parentargs,
                self._self_refs,
            ],
        )
Beispiel #6
0
class ModelImpl(EditableSpaceContainerImpl, Impl):

    if_cls = Model

    def __init__(self, *, system, name):
        Impl.__init__(self, system=system)
        EditableSpaceContainerImpl.__init__(self)

        self.cellgraph = DependencyGraph()
        self.lexdep = DependencyGraph()  # Lexical dependency
        self.spacegraph = SpaceGraph()
        self.currentspace = None

        if not name:
            self.name = system._modelnamer.get_next(system.models)
        elif is_valid_name(name):
            self.name = name
        else:
            raise ValueError("Invalid name '%s'." % name)

        data = {"__builtins__": builtins}
        self._global_refs = RefDict(self, data=data)
        self._spaces = ImplDict(self, SpaceView)
        self._dynamic_bases = {}
        self._dynamic_bases_inverse = {}
        self._dynamic_base_namer = AutoNamer("__Space")
        self._namespace = ImplChainMap(self, BaseView,
                                       [self._spaces, self._global_refs])
        self.allow_none = False
        self.lazy_evals = self._namespace

    def rename(self, name):
        """Rename self. Must be called only by its system."""
        if is_valid_name(name):
            if name not in self.system.models:
                self.name = name
                return True  # Rename success
            else:  # Model name already exists
                return False
        else:
            raise ValueError("Invalid name '%s'." % name)

    def clear_descendants(self, source, clear_source=True):
        """Clear values and nodes calculated from `source`."""
        removed = self.cellgraph.clear_descendants(source, clear_source)
        for node in removed:
            del node[OBJ].data[node[KEY]]

    # TODO
    # def clear_lexdescendants(self, refnode):
    #     """Clear values of cells that refer to `ref`."""

    def clear_obj(self, obj):
        """Clear values and nodes of `obj` and their dependants."""
        removed = self.cellgraph.clear_obj(obj)
        for node in removed:
            del node[OBJ].data[node[KEY]]

    def repr_self(self, add_params=True):
        return self.name

    def repr_parent(self):
        return ""

    @property
    def model(self):
        return self

    @Impl.doc.setter
    def doc(self, value):
        self._doc = value

    @property
    def global_refs(self):
        return self._global_refs.get_updated()

    @property
    def namespace(self):
        return self._namespace.get_updated()

    def close(self):
        self.system.close_model(self)

    def save(self, filepath):
        self.update_lazyevals()
        with open(filepath, "wb") as file:
            pickle.dump(self.interface, file, protocol=4)

    def get_object(self, name):
        """Retrieve an object by a dotted name relative to the model."""
        parts = name.split(".")
        space = self.spaces[parts.pop(0)]
        if parts:
            return space.get_object(".".join(parts))
        else:
            return space

    # ----------------------------------------------------------------------
    # Serialization by pickle

    state_attrs = ([
        "name",
        "cellgraph",
        "lexdep",
        "_namespace",
        "_global_refs",
        "_dynamic_bases",
        "_dynamic_bases_inverse",
        "_dynamic_base_namer",
        "spacegraph",
    ] + BaseSpaceContainerImpl.state_attrs + Impl.state_attrs)

    assert len(state_attrs) == len(set(state_attrs))

    def __getstate__(self):

        state = {
            key: value
            for key, value in self.__dict__.items() if key in self.state_attrs
        }

        graphs = {
            name: graph
            for name, graph in state.items()
            if isinstance(graph, DependencyGraph)
        }

        for gname, graph in graphs.items():
            mapping = {}
            for node in graph:
                name = node[OBJ].get_fullname(omit_model=True)
                if node_has_key(node):
                    mapping[node] = (name, node[KEY])
                else:
                    mapping[node] = name
            state[gname] = nx.relabel_nodes(graph, mapping)

        return state

    def __setstate__(self, state):
        self.__dict__.update(state)

    def restore_state(self, system):
        """Called after unpickling to restore some attributes manually."""
        Impl.restore_state(self, system)
        BaseSpaceContainerImpl.restore_state(self, system)
        mapping = {}
        for node in self.cellgraph:
            if isinstance(node, tuple):
                name, key = node
            else:
                name, key = node, None
            cells = self.get_object(name)
            mapping[node] = get_node(cells, key, None)

        self.cellgraph = nx.relabel_nodes(self.cellgraph, mapping)

    def del_space(self, name):
        space = self.spaces[name]
        self.spaces.del_item(name)

    def _set_space(self, space):

        if space.name in self.spaces:
            self.del_space(space.name)
        elif space.name in self.global_refs:
            raise KeyError("Name '%s' already already assigned" % self.name)

        self.spaces.set_item(space.name, space)

    def del_ref(self, name):
        self.global_refs.del_item(name)

    def get_attr(self, name):
        if name in self.spaces:
            return self.spaces[name].interface
        elif name in self.global_refs:
            return get_interfaces(self.global_refs[name])
        else:
            raise AttributeError("Model '{0}' does not have '{1}'".format(
                self.name, name))

    def set_attr(self, name, value):
        if name in self.spaces:
            raise KeyError("Space named '%s' already exist" % self.name)

        self.global_refs.set_item(name, ReferenceImpl(self, name, value))

    def del_attr(self, name):

        if name in self.spaces:
            self.del_space(name)
        elif name in self.global_refs:
            self.del_ref(name)
        else:
            raise KeyError("Name '%s' not defined" % name)

    def get_dynamic_base(self, bases: tuple):
        """Create of get a base space for a tuple of bases"""

        try:
            return self._dynamic_bases_inverse[bases]
        except KeyError:
            name = self._dynamic_base_namer.get_next(self._dynamic_bases)
            base = self._new_space(name=name)
            self.spacegraph.add_space(base)
            self._dynamic_bases[name] = base
            self._dynamic_bases_inverse[bases] = base
            base.add_bases(bases)
            return base
Beispiel #7
0
    def __init__(self,
                 parent,
                 name,
                 formula=None,
                 refs=None,
                 source=None,
                 arguments=None,
                 doc=None):
        Impl.__init__(self, system=parent.system, doc=doc)
        BaseSpaceContainerImpl.__init__(self)
        Derivable.__init__(self)

        self.name = name
        self.parent = parent
        self.cellsnamer = AutoNamer("Cells")

        self._mro_cache = None
        self.update_mro = True

        if isinstance(source, ModuleType):
            self.source = source.__name__
        else:
            self.source = source

        # ------------------------------------------------------------------
        # Construct member containers

        self._dynamic_spaces = ImplDict(self, SpaceView)
        self._dynamic_subs = []
        self._self_refs = RefDict(self)
        self._cells = CellsDict(self)
        self._static_spaces = SpaceDict(self)
        self._spaces = ImplChainMap(
            self, SpaceView, [self._static_spaces, self._dynamic_spaces])

        self._local_refs = {"_self": self, "_space": self}

        self._refs = self._create_refs(arguments)

        self._namespace_impl = ImplChainMap(
            self, None, [self._cells, self._refs, self._spaces])

        self.lazy_evals = self._namespace_impl

        # ------------------------------------------------------------------
        # Add initial refs members

        if refs is not None:
            refsimpl = {
                name: ReferenceImpl(self, name, value)
                for name, value in refs.items()
            }
            self._self_refs.update(refsimpl)
            self._self_refs.set_update()

        # ------------------------------------------------------------------
        # Construct altfunc after space members are crated

        self.param_spaces = {}
        self.formula = None
        if formula is not None:
            self.set_formula(formula)

        # ------------------------------------------------------------------
        # For repr of LazyEvalDict, LazyEvalImpl

        self._cells.debug_name = "_cells"
        self._static_spaces.debug_name = "_static_spaces"
        self._dynamic_spaces.debug_name = "_dynamic_spaces"
        self._self_refs.debug_name = "_self_refs"
        self._refs.debug_name = "_refs"
        self._namespace_impl.debug_name = "_namespace_impl"
Beispiel #8
0
class BaseSpaceImpl(Derivable, BaseSpaceContainerImpl, Impl):
    """Read-only base Space class

    * Cells container
    * Ref container
    * Namespace
    * Formula container
    * Implement Derivable
    """

    # ----------------------------------------------------------------------
    # Serialization by pickle

    state_attrs = ([
        "_mro_cache",
        "update_mro",
        "_cells",
        "_static_spaces",
        "_dynamic_spaces",
        "_local_refs",
        "_self_refs",
        "_refs",
        "_namespace_impl",
        "param_spaces",
        "formula",
        "cellsnamer",
        "name",
        "source",
        "altfunc",
    ] + Derivable.state_attrs + BaseSpaceContainerImpl.state_attrs +
                   Impl.state_attrs)

    assert len(state_attrs) == len(set(state_attrs))

    def __init__(self,
                 parent,
                 name,
                 formula=None,
                 refs=None,
                 source=None,
                 arguments=None,
                 doc=None):
        Impl.__init__(self, system=parent.system, doc=doc)
        BaseSpaceContainerImpl.__init__(self)
        Derivable.__init__(self)

        self.name = name
        self.parent = parent
        self.cellsnamer = AutoNamer("Cells")

        self._mro_cache = None
        self.update_mro = True

        if isinstance(source, ModuleType):
            self.source = source.__name__
        else:
            self.source = source

        # ------------------------------------------------------------------
        # Construct member containers

        self._dynamic_spaces = ImplDict(self, SpaceView)
        self._dynamic_subs = []
        self._self_refs = RefDict(self)
        self._cells = CellsDict(self)
        self._static_spaces = SpaceDict(self)
        self._spaces = ImplChainMap(
            self, SpaceView, [self._static_spaces, self._dynamic_spaces])

        self._local_refs = {"_self": self, "_space": self}

        self._refs = self._create_refs(arguments)

        self._namespace_impl = ImplChainMap(
            self, None, [self._cells, self._refs, self._spaces])

        self.lazy_evals = self._namespace_impl

        # ------------------------------------------------------------------
        # Add initial refs members

        if refs is not None:
            refsimpl = {
                name: ReferenceImpl(self, name, value)
                for name, value in refs.items()
            }
            self._self_refs.update(refsimpl)
            self._self_refs.set_update()

        # ------------------------------------------------------------------
        # Construct altfunc after space members are crated

        self.param_spaces = {}
        self.formula = None
        if formula is not None:
            self.set_formula(formula)

        # ------------------------------------------------------------------
        # For repr of LazyEvalDict, LazyEvalImpl

        self._cells.debug_name = "_cells"
        self._static_spaces.debug_name = "_static_spaces"
        self._dynamic_spaces.debug_name = "_dynamic_spaces"
        self._self_refs.debug_name = "_self_refs"
        self._refs.debug_name = "_refs"
        self._namespace_impl.debug_name = "_namespace_impl"

    def _create_refs(self, arguments=None):
        raise NotImplementedError

    @property
    def model(self):
        return self.parent.model

    @property
    def cells(self):
        return self._cells.get_updated()

    @property
    def static_spaces(self):
        return self._static_spaces.get_updated()

    @property
    def dynamic_spaces(self):
        return self._dynamic_spaces.get_updated()

    @property
    def refs(self):
        return self._refs.get_updated()

    @property
    def self_refs(self):
        return self._self_refs.get_updated()

    @property
    def local_refs(self):
        return self._local_refs

    @property
    def namespace_impl(self):
        return self._namespace_impl.get_updated()

    @property
    def namespace(self):
        return self._namespace_impl.get_updated().interfaces

    # --- Inheritance properties ---

    @property
    def direct_bases(self):
        """Return an iterator over direct base spaces"""
        return list(self.model.spacegraph.predecessors(self))

    @property
    def self_bases(self):
        raise NotImplementedError

    # Overridden temporarily to add dynamic spaces
    @property
    def parent_bases(self):
        if self.parent.is_model():
            return []
        elif self in self.parent.dynamic_spaces.values():
            return []
        else:
            parent_bases = self.parent.bases
            result = []
            for space in parent_bases:
                bases = self._get_members(space)
                if self.name in bases:
                    result.append(bases[self.name])
            return result

    @staticmethod
    def _get_members(other):
        return other.static_spaces

    @property
    def mro(self):
        if self.update_mro:
            self._mro_cache = self.model.spacegraph.get_mro(self)
            self.update_mro = False

        return self._mro_cache

    @Impl.doc.setter
    def doc(self, value):
        self._doc = value

    def is_base(self, other):
        return self in other.bases

    def is_sub(self, other):
        return other in self.bases

    # --- Dynamic space properties ---

    def is_dynamic(self):
        raise NotImplementedError

    def _set_space(self, space):
        if isinstance(space, RootDynamicSpaceImpl):
            self._dynamic_spaces.set_item(space.name, space)
        else:
            self._static_spaces.set_item(space.name, space)

    def _new_cells(self, name, formula, is_derived, source=None):
        cells = CellsImpl(space=self,
                          name=name,
                          formula=formula,
                          source=source)
        self._cells.set_item(cells.name, cells)
        cells.is_derived = is_derived
        return cells

    def _new_ref(self, name, value, is_derived):
        ref = ReferenceImpl(self, name, value)
        self.self_refs.set_item(name, ref)
        ref.is_derived = is_derived
        return ref

    # ----------------------------------------------------------------------
    # Reference operation

    def inherit(self, **kwargs):

        if "event" in kwargs:
            event = kwargs["event"]
        else:
            event = None

        if self.bases and self.bases[0].formula is not None:
            if not isinstance(self, RootDynamicSpaceImpl):
                if event != "new_cells" and event != "cells_set_formula":
                    self.set_formula(self.bases[0].formula)

        attrs = ("cells", "self_refs", "static_spaces")
        for attr in attrs:
            selfmap = getattr(self, attr)
            basemap = ChainMap(*[getattr(base, attr) for base in self.bases])
            for name in basemap:
                if name not in selfmap or selfmap[name].is_derived:
                    if name not in self.namespace_impl:
                        selfmap[name] = self._new_member(attr,
                                                         name,
                                                         is_derived=True)
                        clear_value = False
                    else:
                        if "clear_value" in kwargs:
                            clear_value = kwargs["clear_value"]
                        else:
                            clear_value = True

                    kwargs["clear_value"] = clear_value
                    selfmap[name].inherit(**kwargs)

            names = set(selfmap) - set(basemap)
            for name in names:
                member = selfmap[name]
                if member.is_derived:
                    selfmap.del_item(name)
                    if attr == "static_spaces":
                        self.model.spacegraph.remove_node(member)
                else:
                    member.inherit(**kwargs)

        for dynspace in self._dynamic_subs:
            dynspace.inherit(**kwargs)

    def _new_space_member(self, name, is_derived):
        raise NotImplementedError

    def _new_member(self, attr, name, is_derived=False):
        if attr == "static_spaces":
            return self._new_space_member(name, is_derived)
        elif attr == "cells":
            return self._new_cells(name, formula=None, is_derived=is_derived)
        elif attr == "self_refs":
            return self._new_ref(name, None, is_derived=is_derived)
        else:
            raise RuntimeError("must not happen")

    # ----------------------------------------------------------------------
    # Component properties

    def has_descendant(self, other):
        if self.spaces:
            if other in self.spaces.values():
                return True
            else:
                return any(
                    child.has_descendant(other)
                    for child in self.spaces.values())
        else:
            return False

    def has_linealrel(self, other):
        return self.has_ascendant(other) or self.has_descendant(other)

    def get_object(self, name):
        """Retrieve an object by a dotted name relative to the space."""

        parts = name.split(".")
        child = parts.pop(0)

        if parts:
            return self.spaces[child].get_object(".".join(parts))
        else:
            return self._namespace_impl[child]

    # ----------------------------------------------------------------------
    # Dynamic Space Operation

    def set_formula(self, formula):

        if formula is None:
            if self.formula is not None:
                self.altfunc = self.formula = None
        else:
            if self.formula is None:
                if isinstance(formula, ParamFunc):
                    self.formula = formula
                else:
                    self.formula = ParamFunc(formula, name="_formula")
                self.altfunc = BoundFunction(self)
            else:
                raise ValueError("formula already assigned.")

    def eval_formula(self, node):
        return self.altfunc.get_updated().altfunc(*node[KEY])

    def _get_dynamic_base(self, bases_):
        """Create or get the base space from a list of spaces

        if a direct base space in `bases` is dynamic, replace it with
        its base.
        """
        bases = tuple(base.bases[0] if base.is_dynamic() else base
                      for base in bases_)

        if len(bases) == 1:
            return bases[0]

        elif len(bases) > 1:
            return self.model.get_dynamic_base(bases)

        else:
            RuntimeError("must not happen")

    def _new_dynspace(
        self,
        name=None,
        bases=None,
        formula=None,
        refs=None,
        arguments=None,
        source=None,
    ):
        """Create a new dynamic root space."""

        if name is None:
            name = self.spacenamer.get_next(self.namespace)

        if name in self.namespace:
            raise ValueError("Name '%s' already exists." % name)

        if not is_valid_name(name):
            raise ValueError("Invalid name '%s'." % name)

        space = RootDynamicSpaceImpl(
            parent=self,
            name=name,
            formula=formula,
            refs=refs,
            source=source,
            arguments=arguments,
        )
        space.is_derived = False
        self._set_space(space)

        if bases:  # i.e. not []
            dynbase = self._get_dynamic_base(bases)
            space._dynbase = dynbase
            dynbase._dynamic_subs.append(space)

        return space

    def get_dynspace(self, args, kwargs=None):
        """Create a dynamic root space

        Called from interface methods
        """

        node = get_node(self, *convert_args(args, kwargs))
        key = node[KEY]

        if key in self.param_spaces:
            return self.param_spaces[key]

        else:
            last_self = self.system.self
            self.system.self = self

            try:
                space_args = self.eval_formula(node)

            finally:
                self.system.self = last_self

            if space_args is None:
                space_args = {"bases": [self]}  # Default
            else:
                if "bases" in space_args:
                    bases = get_impls(space_args["bases"])
                    if isinstance(bases, StaticSpaceImpl):
                        space_args["bases"] = [bases]
                    elif bases is None:
                        space_args["bases"] = [self]  # Default
                    else:
                        space_args["bases"] = bases
                else:
                    space_args["bases"] = [self]

            space_args["arguments"] = node_get_args(node)
            space = self._new_dynspace(**space_args)
            self.param_spaces[key] = space
            space.inherit(clear_value=False)
            return space

    # ----------------------------------------------------------------------
    # repr methods

    def repr_self(self, add_params=True):
        return self.name

    def repr_parent(self):
        if self.parent.repr_parent():
            return self.parent.repr_parent() + "." + self.parent.repr_self()
        else:
            return self.parent.repr_self()

    # ----------------------------------------------------------------------
    # Space properties

    def __getstate__(self):
        state = {
            key: value
            for key, value in self.__dict__.items() if key in self.state_attrs
        }
        return state

    def __setstate__(self, state):
        self.__dict__.update(state)

    def restore_state(self, system):
        """Called after unpickling to restore some attributes manually."""
        Impl.restore_state(self, system)
        BaseSpaceContainerImpl.restore_state(self, system)

        for cells in self._cells.values():
            cells.restore_state(system)

    # ----------------------------------------------------------------------
    # Pandas, Module, Excel I/O

    def to_frame(self, args):
        return _to_frame_inner(self.cells, args)
Beispiel #9
0
 def _create_refs(self, arguments=None):
     return ImplChainMap(
         self,
         RefView,
         [self.model._global_refs, self._local_refs, self._self_refs],
     )