Skip to content

Commit 61e5e8f

Browse files
committed
FIX/BREAK(fnop.test): Aliases TC insidiously broken >
and missed `as_renames()`misparsed DICT ALIASES with 2-CHAR KEYS. - BREAK(op) stop accepting 2-element aliases (cwmust always be an iterable). - test: enhanced the TC that got the bug in the first place and the short-circuited one. - test(plan) lazily create parametrized operations, in case code breaks while developing it. - x3 + x2 TCs fail.
1 parent 2fe9574 commit 61e5e8f

File tree

4 files changed

+48
-45
lines changed

4 files changed

+48
-45
lines changed

docs/source/operations.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ Sometimes, you need to interface functions & operations where they name a
196196
>>> op = operation(str,
197197
... name="cloning `provides` with an `alias`",
198198
... provides="real thing",
199-
... aliases=("real thing", "clone"))
199+
... aliases={"real thing": "clone"})
200200

201201
.. graphtik::
202202

graphtik/fnop.py

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def identity_fn(*args, **kwargs):
7878

7979
def as_renames(i, argname):
8080
"""
81-
Parses a list of (source-->destination) from dict, list-of-2-items, single 2-tuple.
81+
Parses a list of (source-->destination) from dict or list-of-2-items.
8282
8383
:return:
8484
a (possibly empty)list-of-pairs
@@ -89,25 +89,15 @@ def as_renames(i, argname):
8989
if not i:
9090
return ()
9191

92-
def is_list_of_2(i):
93-
try:
94-
return all(len(ii) == 2 for ii in i)
95-
except Exception:
96-
pass # Let it be, it may be a dictionary...
97-
98-
if isinstance(i, tuple) and len(i) == 2:
99-
i = [i]
100-
elif not isinstance(i, cabc.Collection):
101-
raise TypeError(
102-
f"Argument {argname} must be a list of 2-element items, was: {i!r}"
103-
) from None
104-
elif not is_list_of_2(i):
105-
try:
106-
i = list(dict(i).items())
107-
except Exception as ex:
108-
raise ValueError(f"Cannot dict-ize {argname}({i!r}) due to: {ex}") from None
92+
try:
93+
i = i.items()
94+
except Exception as ex:
95+
if not isinstance(i, cabc.Collection) or not all(len(pair) == 2 for pair in i):
96+
raise TypeError(
97+
f"Argument {argname} must be a list of 2-element items, was: {i!r}"
98+
) from None
10999

110-
return i
100+
return list(i)
111101

112102

113103
def prefixed(dep, cwd):
@@ -1042,7 +1032,9 @@ def operation(
10421032
10431033
10441034
:param aliases:
1045-
an optional mapping of `provides` to additional ones
1035+
an optional mapping of `provides` to additional ones; if you need to map
1036+
the same *source* provides into multiple *destinations*, use a list of tuples,
1037+
like: ``aliases=[("a", "A1"), ("a", "A2")]``.
10461038
:param cwd:
10471039
The :term:`current-working-document`, when given, all non-root `dependencies`
10481040
(`needs`, `provides` & `aliases`) become :term:`jsonp`\\s, prefixed with this.

test/test_op.py

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
keyword,
2020
operation,
2121
optional,
22-
token,
2322
sfxed,
23+
token,
2424
vararg,
2525
varargs,
2626
vcat,
@@ -347,16 +347,32 @@ def test_pipeline_merge_node_props():
347347
@pytest.mark.parametrize(
348348
"inp, exp",
349349
[
350-
({"a": "b"}, {"a": "b"}.items()),
351-
((1, 2), [(1, 2)]),
350+
([("", "")], [("", "")]),
351+
({"": ""}, [("", "")]),
352+
([("a", "b")], [("a", "b")]),
353+
((("a", "b"),), [("a", "b")]),
354+
([("ab", "b")], [("ab", "b")]),
355+
([("abc", "b")], [("abc", "b")]),
356+
({"a": "b"}, [("a", "b")]),
357+
({"ab": "b"}, [("ab", "b")]),
358+
({"abc": "b"}, [("abc", "b")]),
359+
([(token("a"), "b")], [(token("a"), "b")]),
360+
({token("a"): "b"}, [(token("a"), "b")]),
361+
({"abc": "b"}, [("abc", "b")]),
362+
({(1, 2)}, [(1, 2)]),
352363
([(1, 2)], [(1, 2)]),
353-
([], []),
354-
((), []),
355-
(("ab", "ad"), [("a", "b"), ("a", "d")]),
364+
([], ()),
365+
((), ()),
366+
([("a", "1"), ("a", "2")], [("a", "1"), ("a", "2")]),
367+
([("a", "3"), ("a", "2"), ("a", "1")], [("a", "3"), ("a", "2"), ("a", "1")]),
368+
([("a", "c"), ("e", "g")], [("a", "c"), ("e", "g")]),
369+
({"a": "c", "e": "g"}, [("a", "c"), ("e", "g")]),
370+
([("ab", "cd"), ("ef", "gh")], [("ab", "cd"), ("ef", "gh")]),
371+
({"ab": "cd", "ef": "gh"}, [("ab", "cd"), ("ef", "gh")]),
356372
],
357373
)
358374
def test_as_renames(inp, exp):
359-
as_renames((1, 2), "alias")
375+
assert as_renames(inp, "alias") == exp
360376

361377

362378
@pytest.mark.parametrize(
@@ -386,14 +402,9 @@ def test_as_renames(inp, exp):
386402
r"The `aliases` \['b'-->'B'\] rename non-existent provides in \['a'\]"
387403
),
388404
),
389-
(
390-
(token("a"), {token("a"): "a"}),
391-
ValueError("must not contain `tokens`"),
392-
),
393-
(
394-
("a", {"a": token("AA")}),
395-
ValueError("must not contain `tokens`"),
396-
),
405+
((token("a"), {token("a"): "a"}), ValueError("must not contain `tokens`")),
406+
(("a", {"a": token("AA")}), ValueError("must not contain `tokens`")),
407+
(("a", {token("a"): "b"}), ValueError("rename non-existent provides")),
397408
(
398409
(["a", "b"], {"a": "b"}),
399410
ValueError(r"clash with existing provides in \['a', 'b'\]"),

test/test_planning.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,33 +150,33 @@ def test_yield_chained_docs_root(g):
150150
"ops, err",
151151
[
152152
(
153-
[operation(str, "BOOM", provides="BOOM")],
153+
lambda: [operation(str, "BOOM", provides="BOOM")],
154154
r"Name of provides\('BOOM'\) clashed with a same-named operation",
155155
),
156156
(
157-
[operation(str, "BOOM", needs="BOOM")],
157+
lambda: [operation(str, "BOOM", needs="BOOM")],
158158
r"Name of operation\(BOOM\) clashed with a same-named dependency",
159159
),
160160
(
161-
[operation(str, "BOOM", provides="a", aliases=("a", "BOOM"))],
161+
lambda: [operation(str, "BOOM", provides="a", aliases={"a": "BOOM"})],
162162
r"Name of provides\('BOOM'\) clashed with a same-named operation",
163163
),
164164
(
165-
[operation(str, "op1", provides="BOOM"), operation(str, "BOOM")],
165+
lambda: [operation(str, "op1", provides="BOOM"), operation(str, "BOOM")],
166166
r"Name of operation\(BOOM\) clashed with a same-named dependency",
167167
),
168168
## x2 ops
169169
(
170-
[operation(str, "BOOM"), operation(str, "op2", "BOOM")],
170+
lambda: [operation(str, "BOOM"), operation(str, "op2", "BOOM")],
171171
r"Name of needs\('BOOM'\) clashed with a same-named operation",
172172
),
173173
(
174-
[operation(str, "op1", needs="BOOM"), operation(str, "BOOM")],
174+
lambda: [operation(str, "op1", needs="BOOM"), operation(str, "BOOM")],
175175
r"Name of operation\(BOOM\) clashed with a same-named dependency",
176176
),
177177
(
178-
[
179-
operation(str, "op1", provides="a", aliases=("a", "BOOM")),
178+
lambda: [
179+
operation(str, "op1", provides="a", aliases={"a": "BOOM"}),
180180
operation(str, "BOOM"),
181181
],
182182
r"Name of operation\(BOOM\) clashed with a same-named dependency",
@@ -185,4 +185,4 @@ def test_yield_chained_docs_root(g):
185185
)
186186
def test_node_clashes(ops, err):
187187
with pytest.raises(ValueError, match=err):
188-
Network(*ops)
188+
Network(*ops())

0 commit comments

Comments
 (0)