Skip to content

Commit 805b809

Browse files
committed
WIP CWD
1 parent bb8e77c commit 805b809

File tree

4 files changed

+91
-6
lines changed

4 files changed

+91
-6
lines changed

docs/source/arch.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,9 @@ Architecture
515515
For instance, if the root has been asked as output, no subdoc can be
516516
subsequently `evicted <eviction>`.
517517

518+
Note that `jsonp` are implicitly created on an operation that has
519+
a `current-working-document` defined.
520+
518521
:seealso: ::ref:`hierarchical-data` (example)
519522

520523
json pointer path
@@ -526,6 +529,10 @@ Architecture
526529
In addition to writing values, the :func:`.vcat` or :func:`.hcat` modifiers
527530
(& respective accessors) support also `pandas concatenation` for `provides`.
528531

532+
cwd
533+
current-working-document
534+
A `jsonp` prefix of an `operation` to prefix any non-root `dependency` defined for it.
535+
529536
pandas concatenation
530537
A `jsonp` `dependency` in `provides` may `designate <modifier>` its respective
531538
:class:`~pandas.DataFrame` and/or :class:`~pandas.Series` `output value <outputs>`

docs/source/pipelines.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,12 @@ which is a nested dictionary:
603603
'weekly_tasks': (range(0, 4), range(4, 9)),
604604
'todos': ()}
605605

606+
.. tip::
607+
If an operation works with dependencies only in some sub-document and below,
608+
its prefix can be factored-out as a :term:`current-working-document`, an argument
609+
given when defining the operation.
610+
611+
606612
.. _jsnop-df-concat:
607613

608614
Concatenating Pandas

graphtik/fnop.py

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import textwrap
1515
from collections import abc as cabc
1616
from functools import update_wrapper, wraps
17-
from typing import Any, Callable, Collection, List, Mapping, Tuple
17+
from typing import Any, Callable, Collection, List, Mapping, Sequence, Tuple
1818

1919
from boltons.setutils import IndexedSet as iset
2020

@@ -202,11 +202,15 @@ def reparse_operation_data(
202202
return name, jsonp_ize_all(needs), jsonp_ize_all(provides), aliases
203203

204204

205-
def _spread_sideffects(
206-
deps: Collection[str],
205+
def _process_dependencies(
206+
deps: Collection[str], cwd: Sequence[str]
207207
) -> Tuple[Collection[str], Collection[str]]:
208208
"""
209-
Build fn/op dependencies by stripping/singularizing any :term:`implicit`/:term:`sideffects`.
209+
Strip or singularize any :term:`implicit`/:term:`sideffects` and apply CWD.
210+
211+
:param cwd:
212+
the :term:`current-working-document` parts to prepend on each dependency,
213+
turning all as :term:`jsonp`\\s.
210214
211215
:return:
212216
a x2 tuple ``(op_deps, fn_deps)``, where any instances of
@@ -230,6 +234,20 @@ def _spread_sideffects(
230234
#: The dedupe any `sideffected`.
231235
seen_sideffecteds = set()
232236

237+
def prefixed(dep):
238+
"""
239+
Converts `dep` into a :term:`jsonp` and prepends `prefix` (unless `dep` was rooted).
240+
241+
TODO: make `prefixed` a TOP_LEVEL `modifier`.
242+
"""
243+
from .jsonpointer import jsonp_path, json_pointer, prepend_parts
244+
245+
if cwd and not is_pure_sfx(dep):
246+
parts = prepend_parts(cwd, jsonp_path(dep_stripped(dep)))
247+
dep = dep_renamed(dep, json_pointer(parts), jsonp=parts)
248+
249+
return dep
250+
233251
def as_fn_deps(dep):
234252
"""Strip and dedupe any sfxed, drop any sfx and implicit. """
235253
if is_implicit(dep): # must ignore also `sfxed`s
@@ -246,6 +264,7 @@ def as_fn_deps(dep):
246264
assert deps is not None
247265

248266
if deps:
267+
deps = [prefixed(d) for d in deps]
249268
op_deps = iset(nn for n in deps for nn in dep_singularized(n))
250269
fn_deps = tuple(nn for n in deps for nn in as_fn_deps(n))
251270
return op_deps, fn_deps
@@ -304,6 +323,7 @@ def __init__(
304323
provides: Items = None,
305324
aliases: Mapping = None,
306325
*,
326+
cwd=None,
307327
rescheduled=None,
308328
endured=None,
309329
parallel=None,
@@ -317,6 +337,8 @@ def __init__(
317337
See :func:`.operation` for the full documentation of parameters,
318338
study the code for attributes (or read them from rendered sphinx site).
319339
"""
340+
from .jsonpointer import jsonp_path
341+
320342
super().__init__()
321343
node_props = node_props = node_props if node_props else {}
322344

@@ -333,10 +355,11 @@ def __init__(
333355
name, needs, provides, aliases = reparse_operation_data(
334356
name, needs, provides, aliases
335357
)
358+
cwd_parts = jsonp_path(cwd) if cwd else ()
336359

337360
user_needs, user_provides = needs, provides
338-
needs, _fn_needs = _spread_sideffects(needs)
339-
provides, _fn_provides = _spread_sideffects(provides)
361+
needs, _fn_needs = _process_dependencies(needs, cwd_parts)
362+
provides, _fn_provides = _process_dependencies(provides, cwd_parts)
340363
alias_dst = aliases and tuple(dst for _src, dst in aliases)
341364
provides = iset((*provides, *alias_dst))
342365

@@ -398,6 +421,9 @@ def __init__(
398421
#:
399422
#: You cannot alias an :term:`alias`.
400423
self.aliases = aliases
424+
#: The :term:`current-working-document`, when defined, all non-root `dependencies`
425+
# become :term:`jsonp` and are prefixed with this.
426+
self.cwd = cwd
401427
#: If true, underlying *callable* may produce a subset of `provides`,
402428
#: and the :term:`plan` must then :term:`reschedule` after the operation
403429
#: has executed. In that case, it makes more sense for the *callable*
@@ -487,6 +513,7 @@ def withset(
487513
provides: Items = ...,
488514
aliases: Mapping = ...,
489515
*,
516+
cwd=...,
490517
rescheduled=...,
491518
endured=...,
492519
parallel=...,
@@ -921,6 +948,7 @@ def operation(
921948
provides: Items = UNSET,
922949
aliases: Mapping = UNSET,
923950
*,
951+
cwd=UNSET,
924952
rescheduled=UNSET,
925953
endured=UNSET,
926954
parallel=UNSET,
@@ -1002,6 +1030,9 @@ def operation(
10021030
10031031
:param aliases:
10041032
an optional mapping of `provides` to additional ones
1033+
:param cwd:
1034+
The :term:`current-working-document`, when given, all non-root `dependencies`
1035+
become :term:`jsonp` and are prefixed with this.
10051036
:param rescheduled:
10061037
If true, underlying *callable* may produce a subset of `provides`,
10071038
and the :term:`plan` must then :term:`reschedule` after the operation

test/test_op.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
sfxed,
2424
vararg,
2525
varargs,
26+
vcat,
2627
)
2728
from graphtik.config import (
2829
operations_endured,
@@ -421,6 +422,46 @@ def test_keyword_jsonp():
421422
assert sol == {"a": "ciaociao"}
422423

423424

425+
def test_cwd():
426+
op = operation(
427+
str,
428+
None,
429+
needs=[
430+
"a",
431+
"a/b",
432+
"/r/b",
433+
optional("o"),
434+
keyword("k"),
435+
implicit("i"),
436+
vararg("v1"),
437+
varargs("v2"),
438+
sfx("s1"),
439+
sfxed("s2", "s22"),
440+
vcat("vc"),
441+
],
442+
provides=["A/B", "C", "/R"],
443+
cwd="root",
444+
)
445+
exp = """
446+
FnOp(name='str',
447+
needs=['root/a'($),
448+
'root/a/b'($),
449+
'/r/b'($),
450+
'root/o'($?'o'),
451+
'root/k'($>'k'),
452+
'root/i'($),
453+
'root/v1'($*),
454+
'root/v2'($+),
455+
sfx('s1'),
456+
sfxed('root/s2'($),
457+
's22'),
458+
'root/vc'($)],
459+
provides=['root/A/B'($), 'root/C'($), '/R'($)],
460+
fn='str')
461+
"""
462+
assert oneliner(op) == oneliner(exp)
463+
464+
424465
@pytest.mark.parametrize(
425466
"provide, aliases, exp",
426467
[

0 commit comments

Comments
 (0)