diff --git a/subtrees/dagflow/subtrees/gindex/gindex/gnindex.py b/subtrees/dagflow/subtrees/gindex/gindex/gnindex.py index 605ca28847b854da04f197970aa7146e828c5380..3a4e95586b5ea7a8b4ccf3284d0e38b342fa87db 100644 --- a/subtrees/dagflow/subtrees/gindex/gindex/gnindex.py +++ b/subtrees/dagflow/subtrees/gindex/gindex/gnindex.py @@ -56,7 +56,7 @@ class GIndexNameDict(UserDict): @define(hash=True, slots=True) class GNIndexInstance: """ - The n-dimensional index instance class, storing `values` + The n-dimensional index instance class, storing `indices` (`type=list[GIndexInstance]`) and `names` (`type=dict[GIndexName, ]`). Contains `format` method, which substitutes `value` instead of `name.short` and `name.full`. @@ -66,7 +66,7 @@ class GNIndexInstance: that is the `string` for formatting """ - _instances: Tuple[GIndexInstance, ...] = field(default=tuple(), alias='instances') + _indices: Tuple[GIndexInstance, ...] = field(default=tuple(), alias='indices') order: tuple = field(default=tuple()) sep: str = field(validator=instance_of(str), default="_") withname: bool = field(validator=instance_of(bool), default=False) @@ -81,10 +81,10 @@ class GNIndexInstance: @property def values(self) -> Tuple[str]: - return tuple(instance.value for instance in self._instances) + return tuple(instance.value for instance in self._indices) def __attrs_post_init__(self) -> None: - self._check_instances() + self._check_indices() if not self.order: self.order = self._auto_order() else: @@ -96,21 +96,21 @@ class GNIndexInstance: ) -> None: if not order: order = self.order - values = [self.dict[name] for name in order if name in self.dict] - if len(self._instances) != len(values): + indices = [self.dict[name] for name in order if name in self.dict] + if len(self._indices) != len(indices): names = set(self.dict.keys()) - set(order) for name in names: if rest2end: - values.append(self.dict[name]) + indices.append(self.dict[name]) else: - values.insert(0, self.dict[name]) - self._instances = tuple(values) + indices.insert(0, self.dict[name]) + self._indices = tuple(indices) def _create_dict(self) -> GIndexNameDict: - return GIndexNameDict({val.name: val for val in self._instances}) + return GIndexNameDict({val.name: val for val in self._indices}) def _auto_order(self) -> tuple: - return (True,) + tuple(val.name.s for val in self._instances) + return (True,) + tuple(val.name.s for val in self._indices) def _check_order(self, order: Sequence) -> None: if not isinstance(order, Sequence): @@ -120,18 +120,18 @@ class GNIndexInstance: elif not isinstance(order, tuple): order = tuple(order) - def _check_instances(self) -> None: - if not isinstance(self._instances, (Sequence, set)): + def _check_indices(self) -> None: + if not isinstance(self._indices, (Sequence, set)): raise TypeError( - f"'values' must be `Sequence`, but given '{type(self._instances)}'!" + f"'indices' must be `Sequence`, but given '{type(self._indices)}'!" ) - elif not all(isinstance(x, GIndexInstance) for x in self._instances): + elif not all(isinstance(x, GIndexInstance) for x in self._indices): raise ValueError( - "'values' must be `Sequence[GIndexInstance]`, " - f"but given '{self._instances}'!" + "'indices' must be `Sequence[GIndexInstance]`, " + f"but given '{self._indices}'!" ) - elif not isinstance(self._instances, tuple): - self._instances = tuple(self._instances) + elif not isinstance(self._indices, tuple): + self._indices = tuple(self._indices) def formatwith( self, @@ -214,60 +214,69 @@ class GNIndexInstance: ) def size(self) -> int: - """Returns the size of the list with values (number of variants)""" - return len(self._instances) + """Returns the size of the list with indices (number of variants)""" + return len(self._indices) def copy(self, deep: bool = False) -> GNIndexInstance: """Returns a copy of the object""" - return ( - GNIndexInstance( - instances=tuple(self._instances), + + if deep: + ret = GNIndexInstance( + indices=tuple(self._indices), order=tuple(self.order), sep=str(self.sep), withname=bool(self.withname), namemode=str(self.namemode), # type: ignore namesep=str(self.namesep), ) - if deep - else GNIndexInstance( - instances=self._instances, + else: + ret = GNIndexInstance( + indices=self._indices, order=self.order, sep=self.sep, withname=self.withname, namemode=self.namemode, namesep=self.namesep, ) - ) + + if kwargs: + raise RuntimeError(f"GNIndexInstance.copy() unparsed arguments: {kwargs}") + + return ret def copywith(self, **kwargs) -> GNIndexInstance: """Returns a copy of the object with updated fields from `kwargs`""" - return ( - GNIndexInstance( - instances=kwargs.pop("values", tuple(self._instances)), + if kwargs.pop("deep", True): + ret = GNIndexInstance( + indices=kwargs.pop("indices", tuple(self._indices)), order=kwargs.pop("order", tuple(self.order)), sep=kwargs.pop("sep", str(self.sep)), withname=kwargs.pop("withname", bool(self.withname)), namemode=kwargs.pop("namemode", str(self.namemode)), namesep=kwargs.pop("namesep", str(self.namesep)), ) - if kwargs.pop("deep", True) - else GNIndexInstance( - instances=kwargs.pop("values", self._instances), + else: + ret = GNIndexInstance( + indices=kwargs.pop("indices", self._indices), order=kwargs.pop("order", self.order), sep=kwargs.pop("sep", self.sep), withname=kwargs.pop("withname", self.withname), namemode=kwargs.pop("namemode", self.namemode), namesep=kwargs.pop("namesep", self.namesep), ) - ) + + if kwargs: + raise RuntimeError(f"GNIndexInstance.copywith() unparsed arguments: {kwargs}") + + return ret def __iter__(self) -> Iterator[GIndexInstance]: - yield from self._instances + yield from self._indices def __getitem__(self, key: int) -> GIndexInstance: if not isinstance(key, int): raise TypeError(f"'key' must be 'int', but given '{type(key)}'!") - return self._instances[key] + return self._indices[key] @define(hash=True, slots=True) @@ -277,7 +286,7 @@ class GNIndex: (set of the 1-dim indices), the indices `order` and usefull methods """ - values: Tuple[GIndex] = field(default=tuple()) + _indices: Tuple[GIndex] = field(default=tuple(), alias='indices') order: tuple = field(default=tuple()) sep: str = field(validator=instance_of(str), default="_") withname: bool = field(validator=instance_of(bool), default=False) @@ -301,7 +310,7 @@ class GNIndex: ) def __attrs_post_init__(self) -> None: - self._check_values() + self._check_indices() if not self.order: self.order = self._auto_order() else: @@ -309,7 +318,7 @@ class GNIndex: self.sort() def _auto_order(self) -> tuple: - return (True,) + tuple(val.name.s for val in self.values) + return (True,) + tuple(val.name.s for val in self._indices) def _check_order(self, order: Sequence) -> None: if not isinstance(order, Sequence): @@ -319,21 +328,21 @@ class GNIndex: elif not isinstance(order, tuple): order = tuple(order) - def _check_values(self) -> None: - if not isinstance(self.values, (Sequence, set)): + def _check_indices(self) -> None: + if not isinstance(self._indices, (Sequence, set)): raise TypeError( - f"'indices' must be `Sequence`, but given '{type(self.values)}'!" + f"'indices' must be `Sequence`, but given '{type(self._indices)}'!" ) - elif not all(isinstance(x, GIndex) for x in self.values): + elif not all(isinstance(x, GIndex) for x in self._indices): raise ValueError( "'indices' must be `Sequence[GIndex]`, " - f"but given '{self.values}'!" + f"but given '{self._indices}'!" ) - elif not isinstance(self.values, tuple): - self.values = tuple(self.values) + elif not isinstance(self._indices, tuple): + self._indices = tuple(self._indices) def _create_dict(self) -> GIndexNameDict: - return GIndexNameDict({val.name: val for val in self.values}) + return GIndexNameDict({val.name: val for val in self._indices}) def rest( self, @@ -353,14 +362,14 @@ class GNIndex: return: A `GNIndex` with tuple of the rest indices """ - if len(self.values) == 0: + if len(self._indices) == 0: return None if isinstance(names, (list, tuple, set)): return ( - self.copywith(values=values) + self.copywith(indices=indices) if len(names) != 0 and ( - values := tuple( + indices := tuple( self._rest(self.dict.copy(), names).values() ) ) @@ -369,7 +378,7 @@ class GNIndex: elif isinstance(names, (str, GIndexName)): tmpdict = self.dict.copy() tmpdict.pop(names) - return self.copywith(values=tuple(tmpdict.values())) + return self.copywith(indices=tuple(tmpdict.values())) raise TypeError( f"'names' must be `Sequence[str]`, but given '{type(names)}'!" ) @@ -418,7 +427,7 @@ class GNIndex: res.extend(self.__split(name) for name in names) else: res.append(self.__split(names)) - return self.copywith(values=tuple(res)) + return self.copywith(indices=tuple(res)) def __split(self, name: Any) -> GIndex: if not isinstance(name, (str, GIndexName)): @@ -431,9 +440,9 @@ class GNIndex: def sub(self, names: tuple) -> GNIndex: return self.copywith( - values=tuple( + indices=tuple( val - for val in self.values + for val in self._indices if (val.name.s in names or val.name.f in names) ) ) @@ -441,15 +450,15 @@ class GNIndex: subindex = sub def union(self, *args, **kwargs) -> GNIndex: - values = [*self.values] + indices = [*self._indices] for arg in args: if not isinstance(arg, GNIndex): raise TypeError( f"'args' must be `GNIndex`, but given '{type(arg)}'" ) - values.extend(value for value in arg.values if value not in values) - return self.copywith(values=values, **kwargs) + indices.extend(index for index in arg._indices if index not in indices) + return self.copywith(indices=indices, **kwargs) def __add__(self, right: GNIndex) -> GNIndex: if not isinstance(right, GNIndex): @@ -461,7 +470,7 @@ class GNIndex: "'right' must have the same `order` as the left," f"but given '{self.order=}', '{right.order=}'" ) - return self.copywith(values=set(self.values + right.values)) + return self.copywith(indices=set(self._indices + right._indices)) def __or__(self, right: GNIndex) -> GNIndex: return self.__add__(right) @@ -476,7 +485,7 @@ class GNIndex: "'right' must have the same `order` as the left," f"but given '{self.order=}', '{right.order=}'" ) - return self.copywith(values=set(self.values) - set(right.values)) + return self.copywith(indices=set(self._indices) - set(right._indices)) def __xor__(self, right: GNIndex) -> GNIndex: return self.__sub__(right) @@ -485,35 +494,35 @@ class GNIndex: if not order: order = self.order tmpdict = self.dict.copy() - values = [tmpdict.pop(name) for name in order if name in tmpdict] - if vals := tmpdict.values(): - values.extend(vals) - self.values = tuple(values) + indices = [tmpdict.pop(name) for name in order if name in tmpdict] + if idxs := tmpdict.values(): + indices.extend(idxs) + self._indices = tuple(indices) def dim(self) -> int: """Returns the dimension of the index (size of the indices list)""" - return len(self.values) + return len(self._indices) def instances(self) -> Tuple[Tuple[GNIndexInstance, ...], ...]: """Returns a tuple of the indices instances tuples (2D version)""" - return tuple(ind.instances() for ind in self.values) + return tuple(ind.instances() for ind in self._indices) def instances1d(self) -> Tuple[GNIndexInstance, ...]: """Returns a tuple of the indices instances (1D version)""" - return tuple(inst for ind in self.values for inst in ind.instances()) + return tuple(inst for ind in self._indices for inst in ind.instances()) def names(self) -> tuple: - return tuple(val.name for val in self.values) + return tuple(val.name for val in self._indices) def names1d( self, namemode: Literal["s", "f", "short", "full"] = "s" ) -> tuple: - return tuple(val.name[namemode] for val in self.values) + return tuple(val.name[namemode] for val in self._indices) def __iter__(self) -> Iterator[GNIndexInstance]: for val in product(*self.instances()): yield GNIndexInstance( - instances=val, # type:ignore + indices=val, # type:ignore order=self.order, sep=self.sep, withname=self.withname, @@ -523,44 +532,53 @@ class GNIndex: def copy(self, deep: bool = False) -> GNIndex: """Returns a copy of the object""" - return ( - GNIndex( - values=tuple(self.values), + if deep: + ret = GNIndex( + indices=tuple(self._indices), order=tuple(self.order), sep=str(self.sep), withname=bool(self.withname), namemode=str(self.namemode), # type:ignore namesep=str(self.namesep), ) - if deep - else GNIndex( - values=self.values, + else: + ret = GNIndex( + indices=self._indices, order=self.order, sep=self.sep, withname=self.withname, namemode=self.namemode, namesep=self.namesep, ) - ) + + if kwargs: + raise RuntimeError(f"GNIndex.copy() unparsed arguments: {kwargs}") + + return ret def copywith(self, **kwargs) -> GNIndex: """Returns a copy of the object with updated fields from `kwargs`""" - return ( - GNIndex( - values=kwargs.pop("values", tuple(self.values)), + + if kwargs.pop("deep", True): + ret = GNIndex( + indices=kwargs.pop("indices", tuple(self._indices)), order=kwargs.pop("order", tuple(self.order)), sep=kwargs.pop("sep", str(self.sep)), withname=kwargs.pop("withname", bool(self.withname)), namemode=kwargs.pop("namemode", str(self.namemode)), namesep=kwargs.pop("namesep", str(self.namesep)), ) - if kwargs.pop("deep", True) - else GNIndex( - values=kwargs.pop("values", self.values), + else: + ret = GNIndex( + indices=kwargs.pop("indices", self._indices), order=kwargs.pop("order", self.order), sep=kwargs.pop("sep", self.sep), withname=kwargs.pop("withname", self.withname), namemode=kwargs.pop("namemode", self.namemode), namesep=kwargs.pop("namesep", self.namesep), ) - ) + + if kwargs: + raise RuntimeError(f"GNIndex.copywith() unparsed arguments: {kwargs}") + + return ret diff --git a/subtrees/dagflow/subtrees/gindex/tests/test_gindex.py b/subtrees/dagflow/subtrees/gindex/tests/test_gindex.py index ae4b1912e3b134060834bda2dce40ae5c7850981..f0e24bdf4d353d2fc31e6cb48871740b611f6095 100644 --- a/subtrees/dagflow/subtrees/gindex/tests/test_gindex.py +++ b/subtrees/dagflow/subtrees/gindex/tests/test_gindex.py @@ -109,7 +109,7 @@ def test_gindex_format(detector01, detector_name): def test_gnindex(detector, subdetector): - nind = GNIndexInstance(instances=(detector[0], subdetector[0])) + nind = GNIndexInstance(indices=(detector[0], subdetector[0])) assert nind assert nind.format(string="Spectrum") == ( f"Spectrum{detector[0].sep}{detector[0].value}" @@ -130,7 +130,7 @@ def test_gnindex(detector, subdetector): def test_gnindex_iter(detector, subdetector, index_values): sep = "_" - nind = GNIndex(values=(detector, subdetector), sep=sep) + nind = GNIndex(indices=(detector, subdetector), sep=sep) nvals = tuple( sep.join(pair) for pair in product(index_values, index_values) ) @@ -141,29 +141,29 @@ def test_gnindex_iter(detector, subdetector, index_values): def test_gnindex_arithmetic(detector, subdetector): gorder = ("det", "subdet", "i") - nind = GNIndex(values=(detector, subdetector), order=gorder) + nind = GNIndex(indices=(detector, subdetector), order=gorder) ind = GIndex(GIndexName("i", "index"), ("1", "2")) - nind2 = GNIndex(values=(detector, ind), order=gorder) - nind3 = GNIndex(values=(ind,), order=gorder) + nind2 = GNIndex(indices=(detector, ind), order=gorder) + nind3 = GNIndex(indices=(ind,), order=gorder) # `sub` and `-` - assert all(x - x == x.copywith(values=tuple()) for x in (nind, nind2)) + assert all(x - x == x.copywith(indices=tuple()) for x in (nind, nind2)) assert all( - x.sub(("new",)) == x.copywith(values=tuple()) for x in (nind, nind2) + x.sub(("new",)) == x.copywith(indices=tuple()) for x in (nind, nind2) ) assert all(x.sub(x.names1d()) == x for x in (nind, nind2)) - assert nind2.sub(("i",)) == nind.copywith(values=(ind,)) + assert nind2.sub(("i",)) == nind.copywith(indices=(ind,)) # `merge` and `+` assert all( - len(x.values) == len(nind.values) - and set(x.values) == set(nind.values) + len(x._indices) == len(nind._indices) + and set(x._indices) == set(nind._indices) and x.order == gorder for x in (nind + nind, nind | nind, nind.union(nind)) ) assert all( (y := nind + nind2) and y == x and y.order == gorder for x in ( - nind.copywith(values={detector, subdetector, ind}), - nind2.copywith(values={detector, subdetector, ind}), + nind.copywith(indices={detector, subdetector, ind}), + nind2.copywith(indices={detector, subdetector, ind}), nind.union(nind3), nind | nind2, ) @@ -176,7 +176,7 @@ def test_gnindex_rest_split( gorder = ("det", "subdet", "i") iname = GIndexName("i", "index") ind = GIndex(iname, ("1", "2")) - nind = GNIndex(values=(detector, subdetector, ind), order=gorder) + nind = GNIndex(indices=(detector, subdetector, ind), order=gorder) # test `dict` assert all( x in nind.dict @@ -197,22 +197,22 @@ def test_gnindex_rest_split( ): assert isinstance(elem, GNIndex) assert elem.order == nind.order - assert elem.values == (subdetector, ind) + assert elem._indices == (subdetector, ind) for elem in ( nind.rest(val) for val in (iname, "i", "index", ("i",), ("index",)) ): assert isinstance(elem, GNIndex) assert elem.order == nind.order - assert elem.values == (detector, subdetector) + assert elem._indices == (detector, subdetector) # test `split` assert nind, None == nind.split(nind.names1d()) - assert nind.copywith(values=tuple()), nind == nind.split(tuple()) + assert nind.copywith(indices=tuple()), nind == nind.split(tuple()) for elem, rest in ( nind.split(val) for val in (("det",), ("detector",), (detector_name,)) ): assert isinstance(elem, GNIndex) and isinstance(rest, GNIndex) assert elem.order == nind.order and rest.order == nind.order - assert elem.values == (detector,) and rest.values == (subdetector, ind) + assert elem._indices == (detector,) and rest._indices == (subdetector, ind) for elem, rest in ( nind.split(val) for val in ( @@ -223,7 +223,7 @@ def test_gnindex_rest_split( ): assert isinstance(elem, GNIndex) and isinstance(rest, GNIndex) assert elem.order == nind.order and rest.order == nind.order - assert elem.values == (subdetector,) and rest.values == (detector, ind) + assert elem._indices == (subdetector,) and rest._indices == (detector, ind) for elem, rest in ( nind.split(val) for val in ( @@ -234,12 +234,12 @@ def test_gnindex_rest_split( ): assert isinstance(elem, GNIndex) and isinstance(rest, GNIndex) assert elem.order == nind.order and rest.order == nind.order - assert elem.values == (detector, subdetector) and rest.values == (ind,) + assert elem._indices == (detector, subdetector) and rest._indices == (ind,) def test_gnindex_order_exception(detector, subdetector, detector_name): orders = (object, 12, {4, 3, 2}, detector_name, detector) with pytest.raises(TypeError): for order in orders: - GNIndexInstance(instances=(detector[0], subdetector[0]), order=order) # type: ignore + GNIndexInstance(indices=(detector[0], subdetector[0]), order=order) # type: ignore GNIndex(values=(detector, subdetector), order=order) # type: ignore