def __init__(self, *, space, name=None, formula=None, data=None, base=None, source=None, is_derived=False): # Determine name if base: name = base.name elif is_valid_name(name): pass elif formula: name = Formula(formula).name if is_valid_name(name): pass else: name = space.cellsnamer.get_next(space.namespace) else: name = space.cellsnamer.get_next(space.namespace) Impl.__init__(self, system=space.system, parent=space, name=name) self.spacemgr = space.spacemgr Derivable.__init__(self, is_derived) self.source = source space._cells.set_item(name, self) # Set formula if base: self.formula = base.formula elif formula is None: self.formula = NullFormula(NULL_FORMULA, name=name) elif isinstance(formula, Formula): self.formula = formula.__class__(formula, name=name) else: self.formula = Formula(formula, name=name) # Set data self.data = {} if data is None: data = {} self.data.update(data) self.input_keys = set(data.keys()) CellsNamespaceReferrer.__init__(self, space) self._namespace = self.parent._namespace if base: self.altfunc = BoundFunction(self, base.altfunc.fresh) else: self.altfunc = BoundFunction(self)
def __init__(self, *, space, name=None, formula=None, data=None, base=None, source=None): Impl.__init__(self, system=space.system) Derivable.__init__(self) self._model = space.model self.space = self.parent = space self.source = source if base: self.name = base.name elif is_valid_name(name): self.name = name elif formula: name = Formula(formula).name if is_valid_name(name): self.name = name else: self.name = space.cellsnamer.get_next(space.namespace) else: self.name = space.cellsnamer.get_next(space.namespace) if base: self.formula = base.formula elif formula is None: self.formula = NullFormula(NULL_FORMULA, name=self.name) elif isinstance(formula, Formula): self.formula = formula.__class__(formula, name=self.name) else: self.formula = Formula(formula, name=self.name) self.data = {} if data is None: data = {} self.data.update(data) self._namespace_impl = self.space._namespace_impl self.altfunc = BoundFunction(self)
def on_rename(self, name): """Renames the Cells name - Clears DynamicCells of self - Updates the parent namespace - Clears successors - Clears DynamicCells of self - Renames sub Cells (Repeats the above for the sub Cells) """ self.model.clear_obj(self) self.parent.cells.delete_item(self.name) self.name = name # Change function name if not self.formula._is_lambda: if self.is_derived: base = self.bases[0] self.formula = base.formula else: self.formula = Formula(self.formula, name=name) self.altfunc = BoundFunction(self) self.parent.cells.add_item(name, self)
class CellsImpl(Derivable, Impl): """ Data container optionally with a formula to set its own values. **Creation** **Deletion** * Values dependent on the cell are deleted clear_all() * Values dependent on the derived cells of the cells are deleted * Derived cells are deleted _del_derived * The cells is deleted _del_self **Changing formula** clear_all _set_formula _clear_all_derived _set_formula_derived **Changing allow_none** **Setting Values** clear() _set **Getting Values** **Deleting Values** clear(params) clear_all _clear_all_derived() Args: space: Space to contain the cell. name: Cell's name. func: Python function or Formula data: array-like, dict, pandas.DataSeries or scalar values. """ if_cls = Cells state_attrs = [ "_model", "space", "formula", "name", "data", "_namespace_impl", "altfunc", ] + Derivable.state_attrs + Impl.state_attrs assert len(state_attrs) == len(set(state_attrs)) def __init__(self, *, space, name=None, formula=None, data=None, base=None, source=None): Impl.__init__(self, system=space.system) Derivable.__init__(self) self._model = space.model self.space = self.parent = space self.source = source if base: self.name = base.name elif is_valid_name(name): self.name = name elif formula: name = Formula(formula).name if is_valid_name(name): self.name = name else: self.name = space.cellsnamer.get_next(space.namespace) else: self.name = space.cellsnamer.get_next(space.namespace) if base: self.formula = base.formula elif formula is None: self.formula = NullFormula(NULL_FORMULA, name=self.name) elif isinstance(formula, Formula): self.formula = formula.__class__(formula, name=self.name) else: self.formula = Formula(formula, name=self.name) self.data = {} if data is None: data = {} self.data.update(data) self._namespace_impl = self.space._namespace_impl self.altfunc = BoundFunction(self) # ---------------------------------------------------------------------- # Serialization by pickle 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) # ---------------------------------------------------------------------- # Properties @property def model(self): return self._model # ---------------------------------------------------------------------- # repr methods def repr_self(self, add_params=True): if add_params: return "%s(%s)" % (self.name, ", ".join(self.formula.parameters)) else: return self.name def repr_parent(self): return self.space.repr_parent() + "." + self.space.repr_self() def has_cell(self, key): return key in self.data def is_scalar(self): # TODO: Move to HasFormula return len(self.formula.parameters) == 0 @property def single_value(self): if self.is_scalar(): return self.get_value(()) else: raise ValueError("%s not a scalar" % self.name) def inherit(self, **kwargs): if "clear_value" in kwargs: clear_value = kwargs["clear_value"] else: clear_value = True if self.bases: if clear_value: self._model.clear_obj(self) self.formula = self.bases[0].formula self.altfunc.set_update() @property def doc(self): if self._doc is None: return self.formula.func.__doc__ else: return self._doc @property def module(self): return self.formula.module @property def self_bases(self): return [] @staticmethod def _get_members(other): return other.cells # ---------------------------------------------------------------------- # Formula operations def reload(self, module=None): oldsrc = self.formula.source newsrc = self.formula._reload(module).source if oldsrc != newsrc: self._model.clear_obj(self) def clear_formula(self): if self.is_derived: self.is_derived = False self.set_formula(NULL_FORMULA) def set_formula(self, func): if self.parent.is_dynamic(): raise ValueError("cannot set formula in dynamic space") if self.is_derived: self.is_derived = False self._model.clear_obj(self) if isinstance(func, Formula): cls = func.__class__ else: cls = Formula self.formula = cls(func, name=self.name) self.altfunc.set_update() self._model.spacegraph.update_subspaces_upward( self.parent, from_parent=False, event="cells_set_formula") # ---------------------------------------------------------------------- # Value operations def on_eval_formula(self, key): value = self.altfunc.get_updated().altfunc(*key) if self.has_cell(key): # Assignment took place inside the cell. if value is not None: raise ValueError("Duplicate assignment for %s" % key) else: value = self.data[key] del self.data[key] value = self._store_value(key, value, False) else: value = self._store_value(key, value, False) return value def get_value(self, args, kwargs=None): node = get_node(self, *convert_args(args, kwargs)) key = node[KEY] if self.has_cell(key): value = self.data[key] else: value = self.system.execution.eval_cell(node) graph = self._model.cellgraph if self.system.callstack: graph.add_path([node, self.system.callstack.last()]) else: graph.add_node(node) return value def find_match(self, args, kwargs): node = get_node(self, *convert_args(args, kwargs)) key = node[KEY] keylen = len(key) if not self.get_property("allow_none"): # raise ValueError('Cells %s cannot return None' % self.name) tracemsg = self.system.callstack.tracemessage() raise NoneReturnedError(node, tracemsg) for match_len in range(keylen, -1, -1): for idxs in combinations(range(keylen), match_len): masked = [None] * keylen for idx in idxs: masked[idx] = key[idx] value = self.get_value(masked) if value is not None: return ArgsValuePair(tuple(masked), value) return ArgsValuePair(None, None) def set_value(self, args, value): node = get_node(self, *convert_args(args, {})) key = node[KEY] if self.system.callstack: if node == self.system.callstack.last(): self._store_value(key, value, False) else: raise KeyError("Assignment in cells other than %s" % key) else: self._store_value(key, value, True) self._model.cellgraph.add_node(node) def _store_value(self, key, value, overwrite=False): if isinstance(value, Cells): if value._impl.is_scalar(): value = value._impl.single_value if not self.has_cell(key) or overwrite: if overwrite: self.clear_value(*key) if value is not None: self.data[key] = value elif self.get_property("allow_none"): self.data[key] = value else: tracemsg = self.system.callstack.tracemessage() raise NoneReturnedError(get_node(self, key, None), tracemsg) else: raise ValueError("Value already exists for %s" % key) return value def clear_value(self, *args, **kwargs): if args == () and kwargs == {} and not self.is_scalar(): self.clear_all_values() else: node = get_node(self, *convert_args(args, kwargs)) if self.has_cell(node[KEY]): self._model.clear_descendants(node) def clear_all_values(self): for args in list(self.data): self.clear_value(*args) # ---------------------------------------------------------------------- # Pandas I/O def tuplize_arg_sequence(self, argseq): if len(argseq) == 1: if isinstance(argseq[0], Sequence) and len(argseq[0]) == 0: pass # Empty sequence else: argseq = argseq[0] for arg in argseq: self.get_value(tuplize_key(self, arg, remove_extra=True)) return tuple(tuplize_key(self, arg) for arg in argseq) def to_series(self, args): from modelx.io.pandas import cells_to_series args = self.tuplize_arg_sequence(args) return cells_to_series(self, args) def to_frame(self, args): from modelx.io.pandas import cells_to_dataframe args = self.tuplize_arg_sequence(args) return cells_to_dataframe(self, args) # ---------------------------------------------------------------------- # Dependency def predecessors(self, args, kwargs): node = get_node(self, *convert_args(args, kwargs)) preds = self._model.cellgraph.predecessors(node) return [CellNode(n) for n in preds] def successors(self, args, kwargs): node = get_node(self, *convert_args(args, kwargs)) succs = self._model.cellgraph.successors(node) return [CellNode(n) for n in succs]
def test_lambda_as_param(): f = Formula(lambdadef2) assert f.func(1) == 3 assert f.source == lambdadef2_extracted
def test_lambda_with_name(): f = Formula(lambdadef1, name="bar") assert f.name == "bar" assert f.func(1) == 12 assert f.source == lambdadef1_extracted
def test_lambda_no_name(): f = Formula(lambdadef1) assert f.name == "<lambda>" assert f.func(1) == 12 assert f.source == lambdadef1_extracted
def test_funcdef_with_name(): f = Formula(funcdef1, name="bar") assert f.name == "bar" assert f.func(1) == 2 assert f.source == funcdef1_renamed
def test_funcdef_no_name(): f = Formula(funcdef1) assert f.name == "foo" assert f.func(1) == 2 assert f.source == funcdef1_nodeco
class CellsImpl(Derivable, Impl): """Cells implementation""" interface_cls = Cells __cls_stateattrs = [ "formula", "data", "_namespace", "altfunc", "source", "input_keys" ] def __init__(self, *, space, name=None, formula=None, data=None, base=None, source=None, is_derived=False): if base: name = base.name elif is_valid_name(name): pass elif formula: name = Formula(formula).name if is_valid_name(name): pass else: name = space.cellsnamer.get_next(space.namespace) else: name = space.cellsnamer.get_next(space.namespace) Impl.__init__(self, system=space.system, parent=space, name=name) Derivable.__init__(self) self.source = source space._cells.set_item(self.name, self) if base: self.formula = base.formula elif formula is None: self.formula = NullFormula(NULL_FORMULA, name=self.name) elif isinstance(formula, Formula): self.formula = formula.__class__(formula, name=self.name) else: self.formula = Formula(formula, name=self.name) self.data = {} if data is None: data = {} self.data.update(data) self._namespace = self.parent._namespace self.altfunc = BoundFunction(self) self.is_derived = is_derived self.input_keys = set(data.keys()) # ---------------------------------------------------------------------- # Serialization by pickle def __getstate__(self): state = { key: value for key, value in self.__dict__.items() if key in self.stateattrs } return state def __setstate__(self, state): self.__dict__.update(state) # ---------------------------------------------------------------------- # repr methods def repr_self(self, add_params=True): if add_params: return "%s(%s)" % (self.name, ", ".join(self.formula.parameters)) else: return self.name def repr_parent(self): return self.parent.repr_parent() + "." + self.parent.repr_self() def has_cell(self, key): return key in self.data def is_scalar(self): # TODO: Move to HasFormula return len(self.formula.parameters) == 0 @property def single_value(self): if self.is_scalar(): return self.get_value(()) else: raise ValueError("%s not a scalar" % self.name) def inherit(self, bases, **kwargs): if "clear_value" in kwargs: clear_value = kwargs["clear_value"] else: clear_value = True if bases: if clear_value: self.model.clear_obj(self) self.formula = bases[0].formula self.altfunc.set_update() @property def namespace(self): return self._namespace.refresh.interfaces @property def doc(self): if self._doc is None: return self.formula.func.__doc__ else: return self._doc @property def module(self): return self.formula.module @staticmethod def _get_members(other): return other.cells # ---------------------------------------------------------------------- # Formula operations def reload(self, module=None): oldsrc = self.formula.source newsrc = self.formula._reload(module).source if oldsrc != newsrc: self.model.clear_obj(self) def clear_formula(self): if self.is_derived: self.is_derived = False self.set_formula(NULL_FORMULA) def set_formula(self, func): if self.parent.is_dynamic(): raise ValueError("cannot set formula in dynamic space") if self.is_derived: self.is_derived = False self.model.clear_obj(self) if isinstance(func, Formula): cls = func.__class__ else: cls = Formula self.formula = cls(func, name=self.name) self.altfunc.set_update() self.model.spacemgr.update_subs(self.parent) # ---------------------------------------------------------------------- # Get/Set values def on_eval_formula(self, key): value = self.altfunc.refresh.altfunc(*key) if self.has_cell(key): # Assignment took place inside the cell. if value is not None: raise ValueError("Duplicate assignment for %s" % key) else: value = self.data[key] else: value = self._store_value(key, value) return value def get_value(self, args, kwargs=None): node = get_node(self, *convert_args(args, kwargs)) key = node[KEY] if self.has_cell(key): value = self.data[key] else: value = self.system.executor.eval_cell(node) graph = self.model.cellgraph if self.system.callstack: graph.add_path([node, self.system.callstack.last()]) else: graph.add_node(node) return value def find_match(self, args, kwargs): node = get_node(self, *convert_args(args, kwargs)) key = node[KEY] keylen = len(key) if not self.get_property("allow_none"): # raise ValueError('Cells %s cannot return None' % self.name) tracemsg = self.system.callstack.tracemessage() raise NoneReturnedError(node, tracemsg) for match_len in range(keylen, -1, -1): for idxs in combinations(range(keylen), match_len): masked = [None] * keylen for idx in idxs: masked[idx] = key[idx] value = self.get_value(masked) if value is not None: return ArgsValuePair(tuple(masked), value) return ArgsValuePair(None, None) def set_value(self, args, value): node = get_node(self, *convert_args(args, {})) key = node[KEY] if self.system.callstack: if node == self.system.callstack.last(): self._store_value(key, value) else: raise KeyError("Assignment in cells other than %s" % key) else: if self.system._recalc_dependents: targets = self.model.cellgraph.get_startnodes_from(node) self.clear_value_at(key) self._store_value(key, value) self.model.cellgraph.add_node(node) self.input_keys.add(key) if self.system._recalc_dependents: for trg in targets: trg[OBJ].get_value(trg[KEY]) def _store_value(self, key, value): if isinstance(value, Cells): if value._impl.is_scalar(): value = value._impl.single_value if value is not None: self.data[key] = value elif self.get_property("allow_none"): self.data[key] = value else: tracemsg = self.system.callstack.tracemessage() raise NoneReturnedError(get_node(self, key, None), tracemsg) return value # ---------------------------------------------------------------------- # Clear value def on_clear_value(self, key): del self.data[key] if key in self.input_keys: self.input_keys.remove(key) def clear_all_values(self, clear_input): for key in list(self.data): if clear_input: self.clear_value_at(key) else: if key not in self.input_keys: self.clear_value_at(key) def clear_value_at(self, key): if self.has_cell(key): self.model.clear_with_descs(key_to_node(self, key)) # ---------------------------------------------------------------------- # Pandas I/O def tuplize_arg_sequence(self, argseq): if len(argseq) == 1: if isinstance(argseq[0], Sequence) and len(argseq[0]) == 0: pass # Empty sequence else: argseq = argseq[0] for arg in argseq: self.get_value(tuplize_key(self, arg, remove_extra=True)) return tuple(tuplize_key(self, arg) for arg in argseq) def to_series(self, args): from modelx.io.pandas import cells_to_series args = self.tuplize_arg_sequence(args) return cells_to_series(self, args) def to_frame(self, args): from modelx.io.pandas import cells_to_dataframe args = self.tuplize_arg_sequence(args) return cells_to_dataframe(self, args) # ---------------------------------------------------------------------- # Dependency def predecessors(self, args, kwargs): node = get_node(self, *convert_args(args, kwargs)) preds = self.model.cellgraph.predecessors(node) return [CellNode(n) for n in preds] def successors(self, args, kwargs): node = get_node(self, *convert_args(args, kwargs)) succs = self.model.cellgraph.successors(node) return [CellNode(n) for n in succs]