Skip to content

Commit 1b014f3

Browse files
committed
REFACT(MODIF) teach The Matrix about Implict(^) ... >
the horror! The horror!! Tests were much less affected. - fix: dissable Accessors for implicits. - doc(arxh): stop rumors about implicits.
1 parent 8021238 commit 1b014f3

File tree

5 files changed

+96
-55
lines changed

5 files changed

+96
-55
lines changed

CHANGES.rst

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,19 @@ Compose
4141

4242
- [+] ++refact: change :term:`accessor` diacritic `` from ``$`` to @``.
4343
- [+] +++feat: add a real ``implicit`` modifier.
44-
- [ ] ++feat: ``^`` diacritic for implicits
45-
- [ ] ++refact: change keyword diacritic to ``**``.
46-
- [ ] refact: rename modifier sfx --> token
47-
- [ ] refact: introduce diacritic for sfx ``$`` (vs ``sfx()``).
44+
- [+] ++feat: ``^`` diacritic for implicits
45+
- [-] ++refact: change keyword diacritic to ``**``.
46+
- NO, ``dep>kword`` is better.
47+
- [+] refact: rename modifier sfx --> token
48+
- [+] refact: introduce diacritic for sfx ``$`` (vs ``sfx()``).
4849
- [ ] refact: change (or separate) jsonp diacritic (from accessor) to ``/`` (``@``)?
4950
- [?] REFACT: separate op-decorator from factory (to facilitate defining conveyor operations).
5051
- [?] `cwd()` modifier:
5152
Isn't already working with relative jsonps?
5253

5354
- `implicit` with only the 2nd arg should work on cwd, no?
5455

56+
- [ ] :func:`.get_accessor()` should return a partial on the dependency.
5557
- [ ] ++FEAT: Teach pipelines how to accept positional arguments with a tuple with their names
5658
- [ ] ++FEAT: +1 merge method for pipelines: nest=False: treat Pipelines as Operations
5759
(need pipeline feat for positional-args, above).

docs/source/arch.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -441,10 +441,10 @@ Architecture
441441
One use case is for an operation to consume/produce a `subdoc`\(s)
442442
with its own means (not through `jsonp` `accessor`\s).
443443

444-
Only a :func:`.modify` & :func:`.sfxed` *modifier* functions accept
445-
the ``implicit`` param.
444+
Constructed with the :func:`.implicit` *modifier* function,
445+
they can also be `optionals` and `jsonp` (but without `accessor`\s).
446+
If an *implicit* cannot solve your problems, try `sideffected` or `tokens`...
446447

447-
If an *implicit* cannot solve your problems, try `sideffects` or `tokens`...
448448

449449
tokens
450450
sideffects
@@ -498,7 +498,7 @@ Architecture
498498

499499
accessor
500500
Getter/setter functions to extract/populate `solution` values given as a `modifier` parameter
501-
(not applicable for pure `tokens`).
501+
(not applicable for `tokens` & `implicit`).
502502

503503
See :class:`.Accessor` defining class and the :func:`.modify` concrete factory.
504504

graphtik/modifier.py

Lines changed: 57 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
+ : :func:`.varargs`
5959
@ : :term:`accessor` (mostly for :term:`jsonp`)
6060
$ : :func:`token`
61+
^ : :func:`implicit`
6162
6263
.. diacritics-end
6364
"""
@@ -80,34 +81,36 @@
8081
#: Arguments-presence patterns for :class:`_Modifier` constructor.
8182
#: Combinations missing raise errors.
8283
_modifier_cstor_matrix = {
83-
# TODO: Add `implicit` in the table, to augment REPR & forbid implicit sfxed.
84-
# (7, kw, opt, accessors, token, sfxed): (STR, REPR, FUNC) OR None
85-
700000: None,
86-
710000: ( "%(dep)s", "'%(dep)s'(%(acs)s>%(kw)s)", "keyword"),
87-
711000: ( "%(dep)s", "'%(dep)s'(%(acs)s?%(kw)s)", "optional"),
88-
702000: ( "%(dep)s", "'%(dep)s'(*)", "vararg"),
89-
703000: ( "%(dep)s", "'%(dep)s'(+)", "varargs"),
84+
# (7, kw, opt, implicit, accessors, sfxed, token/sfx_list): (STR, REPR, FUNC) OR None
85+
7000000: None,
86+
7100000: ( "%(dep)s", "'%(dep)s'(%(acs)s>%(kw)s)", "keyword"),
87+
7110000: ( "%(dep)s", "'%(dep)s'(%(acs)s?%(kw)s)", "optional"),
88+
7020000: ( "%(dep)s", "'%(dep)s'(*)", "vararg"),
89+
7030000: ( "%(dep)s", "'%(dep)s'(+)", "varargs"),
9090
# Accessor
91-
700100: ( "%(dep)s", "'%(dep)s'(@)", "accessor"),
92-
710100: ( "%(dep)s", "'%(dep)s'(@>%(kw)s)", "keyword"),
93-
711100: ( "%(dep)s", "'%(dep)s'(@?%(kw)s)", "optional"),
94-
702100: ( "%(dep)s", "'%(dep)s'(@*)", "vararg"),
95-
703100: ( "%(dep)s", "'%(dep)s'(@+)", "varargs"),
96-
97-
700010: ( "$%(dep)s$", "'%(dep)s'($)", "token"),
98-
701010: ( "$%(dep)s$", "'%(dep)s'($?)", "token"),
91+
7000100: ( "%(dep)s", "'%(dep)s'(@)", "accessor"),
92+
7100100: ( "%(dep)s", "'%(dep)s'(@>%(kw)s)", "keyword"),
93+
7110100: ( "%(dep)s", "'%(dep)s'(@?%(kw)s)", "optional"),
94+
7020100: ( "%(dep)s", "'%(dep)s'(@*)", "vararg"),
95+
7030100: ( "%(dep)s", "'%(dep)s'(@+)", "varargs"),
96+
# Implicit
97+
7001000: ( "%(dep)s", "'%(dep)s'(^)", "implicit"),
98+
7011000: ( "%(dep)s", "'%(dep)s'(^?)", "implicit"),
99+
100+
7000010: ( "$%(dep)s$", "'%(dep)s'($)", "token"),
101+
7010010: ( "$%(dep)s$", "'%(dep)s'($?)", "token"),
99102
#SFXED
100-
700011: ("sfxed('%(dep)s', %(sfx)s)", "sfxed(%(acs)s'%(dep)s', %(sfx)s)", "sfxed"),
101-
710011: ("sfxed('%(dep)s', %(sfx)s)", "sfxed(%(acs)s'%(dep)s'(>%(kw)s), %(sfx)s)", "sfxed"),
102-
711011: ("sfxed('%(dep)s', %(sfx)s)", "sfxed(%(acs)s'%(dep)s'(?%(kw)s), %(sfx)s)", "sfxed"),
103-
702011: ("sfxed('%(dep)s', %(sfx)s)", "sfxed(%(acs)s'%(dep)s'(*), %(sfx)s)", "sfxed_vararg"),
104-
703011: ("sfxed('%(dep)s', %(sfx)s)", "sfxed(%(acs)s'%(dep)s'(+), %(sfx)s)", "sfxed_varargs"),
103+
7000011: ("sfxed('%(dep)s', %(sfx)s)", "sfxed(%(acs)s'%(dep)s', %(sfx)s)", "sfxed"),
104+
7100011: ("sfxed('%(dep)s', %(sfx)s)", "sfxed(%(acs)s'%(dep)s'(>%(kw)s), %(sfx)s)", "sfxed"),
105+
7110011: ("sfxed('%(dep)s', %(sfx)s)", "sfxed(%(acs)s'%(dep)s'(?%(kw)s), %(sfx)s)", "sfxed"),
106+
7020011: ("sfxed('%(dep)s', %(sfx)s)", "sfxed(%(acs)s'%(dep)s'(*), %(sfx)s)", "sfxed_vararg"),
107+
7030011: ("sfxed('%(dep)s', %(sfx)s)", "sfxed(%(acs)s'%(dep)s'(+), %(sfx)s)", "sfxed_varargs"),
105108
# Accessor
106-
700111: ("sfxed('%(dep)s', %(sfx)s)", "sfxed('%(dep)s'(@), %(sfx)s)", "sfxed"),
107-
710111: ("sfxed('%(dep)s', %(sfx)s)", "sfxed('%(dep)s'(@>%(kw)s), %(sfx)s)", "sfxed"),
108-
711111: ("sfxed('%(dep)s', %(sfx)s)", "sfxed('%(dep)s'(@?%(kw)s), %(sfx)s)", "sfxed"),
109-
702111: ("sfxed('%(dep)s', %(sfx)s)", "sfxed('%(dep)s'(@*), %(sfx)s)", "sfxed_vararg"),
110-
703111: ("sfxed('%(dep)s', %(sfx)s)", "sfxed('%(dep)s'(@+), %(sfx)s)", "sfxed_varargs"),
109+
7000111: ("sfxed('%(dep)s', %(sfx)s)", "sfxed('%(dep)s'(@), %(sfx)s)", "sfxed"),
110+
7100111: ("sfxed('%(dep)s', %(sfx)s)", "sfxed('%(dep)s'(@>%(kw)s), %(sfx)s)", "sfxed"),
111+
7110111: ("sfxed('%(dep)s', %(sfx)s)", "sfxed('%(dep)s'(@?%(kw)s), %(sfx)s)", "sfxed"),
112+
7020111: ("sfxed('%(dep)s', %(sfx)s)", "sfxed('%(dep)s'(@*), %(sfx)s)", "sfxed_vararg"),
113+
7030111: ("sfxed('%(dep)s', %(sfx)s)", "sfxed('%(dep)s'(@+), %(sfx)s)", "sfxed_varargs"),
111114
}
112115
# fmt: on
113116

@@ -127,7 +130,7 @@ def _match_modifier_args(name, *args):
127130

128131

129132
class _Optionals(enum.Enum):
130-
keyword = 1
133+
keyword = 1 # or don't bother for tokens & implicits
131134
vararg = 2
132135
varargs = 3
133136

@@ -242,6 +245,7 @@ class _Modifier(str):
242245
#: :func:`is_optional()` returns it.
243246
#: All regulars are `keyword`.
244247
_optional: _Optionals = None
248+
_implicit: bool = None
245249
#: An :term:`accessor` with getter/setter functions to read/write solution values.
246250
#: Any sequence of 2-callables will do.
247251
_accessor: Accessor = None
@@ -262,6 +266,7 @@ def __new__(
262266
_func,
263267
keyword,
264268
optional: _Optionals,
269+
implicit,
265270
accessor,
266271
sideffected,
267272
sfx_list,
@@ -283,13 +288,6 @@ def __new__(
283288
f"`sideffected` cannot be `sfx`, got {sideffected!r}"
284289
f"\n locals={locals()}"
285290
)
286-
287-
# TODO: Add `implicit` in the table, to augment REPR & forbid implicit sfxed.
288-
if sideffected and kw.get("_implicit"):
289-
raise ValueError(
290-
f"`sideffected` cannot be `implicit`, got {sideffected!r}"
291-
f"\n locals={locals()}"
292-
)
293291
double_sideffects = [
294292
f"{type(i).__name__}({i!r})" for i in sfx_list if is_sfx(i)
295293
]
@@ -306,6 +304,8 @@ def __new__(
306304
obj._keyword = keyword
307305
if optional:
308306
obj._optional = optional
307+
if implicit:
308+
obj._implicit = implicit
309309
if accessor:
310310
obj._accessor = accessor
311311
if sideffected:
@@ -331,6 +331,8 @@ def cmd(self):
331331
items.append(f"keyword={keyword}" if self._sfx_list else keyword)
332332
if self._optional == _Optionals.keyword and self._func != "optional":
333333
items.append("optional=1" if self._sfx_list else "1")
334+
if self._implicit:
335+
items.append("implicit=1")
334336
if self._accessor:
335337
items.append(f"accessor={self._accessor!r}")
336338
return f"{self._func}({', '.join(items)})"
@@ -342,6 +344,7 @@ def __getnewargs__(self):
342344
self._func,
343345
self._keyword,
344346
self._optional,
347+
self._implicit,
345348
self._accessor,
346349
self._sideffected,
347350
self._sfx_list,
@@ -353,6 +356,7 @@ def _modifier(
353356
*,
354357
keyword=None,
355358
optional: _Optionals = None,
359+
implicit=None,
356360
accessor=None,
357361
sideffected=None,
358362
sfx_list=(),
@@ -384,11 +388,11 @@ def _modifier(
384388
jsonp = jsonp_path(name)
385389
if jsonp is not None:
386390
kw["_jsonp"] = jsonp
387-
if not accessor and jsonp:
391+
if not accessor and jsonp and not implicit:
388392
# Honor user's accessor.
389393
accessor = JsonpAcc()
390394

391-
args = (name, keyword, optional, accessor, sideffected, sfx_list)
395+
args = (name, keyword, optional, implicit, accessor, sideffected, sfx_list)
392396
formats = _match_modifier_args(*args)
393397

394398
## Private-ize all keywords and throw away nulls.
@@ -398,7 +402,14 @@ def _modifier(
398402
if not formats:
399403
if kw:
400404
# Just jsonp given.
401-
assert not accessor and (optional, accessor, sideffected, sfx_list) == (
405+
assert not accessor and (
406+
optional,
407+
implicit,
408+
accessor,
409+
sideffected,
410+
sfx_list,
411+
) == (
412+
None,
402413
None,
403414
None,
404415
None,
@@ -427,6 +438,7 @@ def modifier_withset(
427438
name=...,
428439
keyword=...,
429440
optional: _Optionals = ...,
441+
implicit=...,
430442
accessor=...,
431443
sideffected=...,
432444
sfx_list=...,
@@ -452,7 +464,7 @@ def modifier_withset(
452464
kw = {
453465
k: v
454466
for k, v in kw.items()
455-
if k not in set(["dep", "name", "kw", "repr", "func"])
467+
if k not in {"dep", "name", "kw", "repr", "func"}
456468
}
457469
if name is ...:
458470
name = dep._sideffected or str(dep)
@@ -741,7 +753,7 @@ class that supports *accessors**, and this requires the operation to be wrapped
741753
)
742754

743755

744-
def implicit(name, *, jsonp=None) -> _Modifier:
756+
def implicit(name, *, optional: bool = None, jsonp=None) -> _Modifier:
745757
"""
746758
:term:`implicit` dependencies are not fed into/out of the function,
747759
usually they are accessed as :term:`jsonp` from some other dependency
@@ -752,7 +764,12 @@ def implicit(name, *, jsonp=None) -> _Modifier:
752764
An *implicit* dependency is expected to always exist in the solution,
753765
contrary to :func:`token`\\s and the :term:`sfx_list` of :func:`sfxed`\\s.
754766
"""
755-
return _modifier(name, implicit=True, jsonp=jsonp)
767+
return _modifier(
768+
name,
769+
implicit=True,
770+
optional=_Optionals.keyword if optional else None,
771+
jsonp=jsonp,
772+
)
756773

757774

758775
def vcat(name, *, keyword: str = None, jsonp=None) -> _Modifier:
@@ -857,7 +874,7 @@ def keyword(
857874

858875

859876
def optional(
860-
name: str, keyword: str = None, accessor: Accessor = None, jsonp=None, implicit=None
877+
name: str, keyword: str = None, accessor: Accessor = None, jsonp=None
861878
) -> _Modifier:
862879
"""
863880
Annotate :term:`optionals` `needs` corresponding to *defaulted* op-function arguments, ...
@@ -878,9 +895,6 @@ def optional(
878895
:param jsonp:
879896
None (derrived from `name`), ``False``, str, collection of str/callable (last one)
880897
See generic :func:`.modify` modifier.
881-
:param implicit:
882-
:term:`implicit` dependencies are not fed into/out of the function.
883-
You may use directly :func:`implicit`.
884898
885899
**Example:**
886900
@@ -928,7 +942,6 @@ def optional(
928942
optional=_Optionals.keyword,
929943
accessor=accessor,
930944
jsonp=jsonp,
931-
implicit=implicit,
932945
)
933946

934947

test/test_modifier.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
dep_renamed,
1212
dep_singularized,
1313
dep_stripped,
14+
get_accessor,
15+
get_jsonp,
16+
get_keyword,
1417
implicit,
1518
is_implicit,
19+
is_optional,
1620
keyword,
1721
modify,
1822
optional,
@@ -343,8 +347,30 @@ def test_implicit(ser_method):
343347
assert is_implicit("a") is None
344348
m = implicit("a")
345349
assert is_implicit(m) is True
346-
m = optional("a", implicit=1)
347-
assert is_implicit(m) is 1
350+
assert get_keyword(m) is None
351+
assert get_keyword(m) is None
352+
353+
m = implicit("a", optional=3)
354+
assert is_implicit(m) is True
355+
assert is_optional(m).value == 1
356+
assert get_keyword(m) is None
357+
358+
m = implicit("a/b")
359+
assert not is_optional(m)
360+
assert get_jsonp(m)
361+
assert get_accessor(m) is None
362+
m = implicit("a/b", jsonp=0)
363+
assert not get_jsonp(m)
364+
assert get_accessor(m) is None
365+
366+
m = implicit("a/b", optional=True)
367+
assert is_optional(m).value == 1
368+
assert get_jsonp(m)
369+
assert get_accessor(m) is None
370+
m = implicit("a/b", optional=True, jsonp=0)
371+
assert is_optional(m).value == 1
372+
assert not get_jsonp(m)
373+
assert get_accessor(m) is None
348374

349375
assert dep_renamed(m, "R")._implicit == m._implicit
350376
assert ser_method(m)._implicit == m._implicit

test/test_op.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ def test_cwd_fnop():
449449
'/r/b'(@),
450450
'root/o'(@?'o'),
451451
'root/k'(@>'k'),
452-
'root/i'(@),
452+
'root/i'(^),
453453
'root/v1'(@*),
454454
'root/v2'(@+),
455455
's1'($),

0 commit comments

Comments
 (0)