|
5 | 5 | import abc |
6 | 6 | import logging |
7 | 7 | from collections import abc as cabc |
| 8 | +from collections import namedtuple |
8 | 9 | from typing import Callable, Mapping, Tuple, Union |
9 | 10 |
|
10 | 11 | from boltons.setutils import IndexedSet as iset |
@@ -60,15 +61,18 @@ def compute(self, named_inputs, outputs=None): |
60 | 61 | """ |
61 | 62 |
|
62 | 63 |
|
63 | | -class FunctionalOperation(Operation): |
| 64 | +class FunctionalOperation( |
| 65 | + namedtuple("FnOp", "fn name needs provides parents node_props returns_dict"), |
| 66 | + Operation, |
| 67 | +): |
64 | 68 | """ |
65 | 69 | An Operation performing a callable (ie function, method, lambda). |
66 | 70 |
|
67 | 71 | Use :class:`operation()` factory to build instances of this class instead. |
68 | 72 | """ |
69 | 73 |
|
70 | | - def __init__( |
71 | | - self, |
| 74 | + def __new__( |
| 75 | + cls, |
72 | 76 | fn: Callable, |
73 | 77 | name, |
74 | 78 | needs: Items = None, |
@@ -98,30 +102,20 @@ def __init__( |
98 | 102 | and no further processing is done on them |
99 | 103 | (i.e. the returned output-values are not zipped with `provides`) |
100 | 104 | """ |
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 {} |
109 | 106 |
|
110 | 107 | 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}") |
114 | 109 | 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}") |
118 | 111 | 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}") |
120 | 113 |
|
121 | 114 | ## Overwrite reparsed op-data. |
122 | 115 | 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 |
125 | 119 | ) |
126 | 120 |
|
127 | 121 | def __eq__(self, other): |
@@ -151,7 +145,13 @@ def __repr__(self): |
151 | 145 | ) |
152 | 146 |
|
153 | 147 | 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 | + """ |
155 | 155 | fn = kw["fn"] if "fn" in kw else self.fn |
156 | 156 | name = kw["name"] if "name" in kw else self.name |
157 | 157 | needs = kw["needs"] if "needs" in kw else self.needs |
|
0 commit comments