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
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 __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()
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)
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, ], )
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
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"
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)
def _create_refs(self, arguments=None): return ImplChainMap( self, RefView, [self.model._global_refs, self._local_refs, self._self_refs], )