Skip to content

Commit f0061a7

Browse files
committed
ENH(op): IMMUTABLE FunctionalOperation` by subclassing namedtuple
1 parent 9a4083c commit f0061a7

File tree

1 file changed

+21
-21
lines changed

1 file changed

+21
-21
lines changed

graphtik/op.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import abc
66
import logging
77
from collections import abc as cabc
8+
from collections import namedtuple
89
from typing import Callable, Mapping, Tuple, Union
910

1011
from boltons.setutils import IndexedSet as iset
@@ -60,15 +61,18 @@ def compute(self, named_inputs, outputs=None):
6061
"""
6162

6263

63-
class FunctionalOperation(Operation):
64+
class FunctionalOperation(
65+
namedtuple("FnOp", "fn name needs provides parents node_props returns_dict"),
66+
Operation,
67+
):
6468
"""
6569
An Operation performing a callable (ie function, method, lambda).
6670
6771
Use :class:`operation()` factory to build instances of this class instead.
6872
"""
6973

70-
def __init__(
71-
self,
74+
def __new__(
75+
cls,
7276
fn: Callable,
7377
name,
7478
needs: Items = None,
@@ -98,30 +102,20 @@ def __init__(
98102
and no further processing is done on them
99103
(i.e. the returned output-values are not zipped with `provides`)
100104
"""
101-
## Set op-data early, for repr() to work on errors.
102-
self.fn = fn
103-
self.name = name
104-
self.needs = needs
105-
self.provides = provides
106-
self.parents = parents
107-
self.node_props = node_props = node_props if node_props else {}
108-
self.returns_dict = returns_dict
105+
node_props = node_props = node_props if node_props else {}
109106

110107
if not fn or not callable(fn):
111-
raise ValueError(
112-
f"Operation was not provided with a callable: {fn}\n {self}"
113-
)
108+
raise ValueError(f"Operation was not provided with a callable: {fn}")
114109
if parents and not isinstance(parents, tuple):
115-
raise ValueError(
116-
f"Operation `parents` must be tuple, was {parents}\n {self}"
117-
)
110+
raise ValueError(f"Operation `parents` must be tuple, was {parents}")
118111
if node_props is not None and not isinstance(node_props, cabc.Mapping):
119-
raise ValueError(f"`node_props` must be a mapping, got: {node_props!r}")
112+
raise ValueError(f"Operation `node_props` must be a dict, was {node_props}")
120113

121114
## Overwrite reparsed op-data.
122115
name = ".".join(str(pop) for pop in ((parents or ()) + (name,)))
123-
self.name, self.needs, self.provides = reparse_operation_data(
124-
name, needs, provides
116+
name, needs, provides = reparse_operation_data(name, needs, provides)
117+
return super().__new__(
118+
cls, fn, name, needs, provides, parents, node_props, returns_dict
125119
)
126120

127121
def __eq__(self, other):
@@ -151,7 +145,13 @@ def __repr__(self):
151145
)
152146

153147
def withset(self, **kw) -> "FunctionalOperation":
154-
"""Make a clone with the some values replaced."""
148+
"""
149+
Make a clone with the some values replaced.
150+
151+
.. ATTENTION::
152+
Using :meth:`namedtuple._replace()` would not pass through cstor,
153+
so would not get a nested `name` with `parents`, not arguments validation.
154+
"""
155155
fn = kw["fn"] if "fn" in kw else self.fn
156156
name = kw["name"] if "name" in kw else self.name
157157
needs = kw["needs"] if "needs" in kw else self.needs

0 commit comments

Comments
 (0)