j-perm 0.2.0.1__py3-none-any.whl → 0.2.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
j_perm/__init__.py CHANGED
@@ -3,19 +3,23 @@ from . import casters as _builtin_casters # noqa: F401
3
3
  from . import constructs as _builtin_constructs # noqa: F401
4
4
  from . import funcs as _builtin_funcs # noqa: F401
5
5
  from . import ops as _builtin_ops # noqa: F401
6
+ from . import shorthands as _builtin_shorthands # noqa: F401
6
7
  from .engine import apply_actions, ActionEngine
7
- from .subst import JpFuncRegistry, CasterRegistry, TemplateSubstitutor
8
+ from .normalizer import Normalizer, ShorthandRegistry
8
9
  from .op_handler import OpRegistry, Handlers
9
10
  from .special_resolver import SpecialRegistry, SpecialResolver
11
+ from .subst import JpFuncRegistry, CasterRegistry, TemplateSubstitutor
10
12
 
11
13
  __all__ = [
12
14
  "apply_actions",
13
15
  "ActionEngine",
14
- "JpFuncRegistry",
15
- "CasterRegistry",
16
- "TemplateSubstitutor",
16
+ "Normalizer",
17
+ "ShorthandRegistry",
17
18
  "OpRegistry",
18
19
  "Handlers",
19
20
  "SpecialRegistry",
20
21
  "SpecialResolver",
22
+ "JpFuncRegistry",
23
+ "CasterRegistry",
24
+ "TemplateSubstitutor",
21
25
  ]
j_perm/constructs/eval.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Mapping, Any
4
4
 
5
- from j_perm.special_resolver import SpecialRegistry
5
+ from ..special_resolver import SpecialRegistry
6
6
 
7
7
 
8
8
  @SpecialRegistry.register("$eval")
j_perm/constructs/ref.py CHANGED
@@ -3,8 +3,8 @@ from __future__ import annotations
3
3
  import copy
4
4
  from typing import Mapping, Any
5
5
 
6
- from j_perm.special_resolver import _MISSING, SpecialRegistry
7
- from j_perm.utils.pointers import maybe_slice
6
+ from ..special_resolver import _MISSING, SpecialRegistry
7
+ from ..utils.pointers import maybe_slice
8
8
 
9
9
 
10
10
  @SpecialRegistry.register("$ref")
j_perm/engine.py CHANGED
@@ -4,6 +4,7 @@ import copy
4
4
  from dataclasses import dataclass, field
5
5
  from typing import Any, List, Mapping, TypeAlias, MutableMapping, Union
6
6
 
7
+ from .normalizer import Normalizer
7
8
  from .op_handler import Handlers
8
9
  from .special_resolver import SpecialResolver
9
10
  from .subst import TemplateSubstitutor
@@ -17,14 +18,6 @@ JsonLikeDest: TypeAlias = Union[MutableMapping[str, Any], List[Any]]
17
18
  # Small utilities
18
19
  # =============================================================================
19
20
 
20
- def _is_pointer_string(v: Any) -> bool:
21
- return isinstance(v, str) and v.startswith("/")
22
-
23
-
24
- def _to_list(x: Any) -> List[Any]:
25
- return x if isinstance(x, list) else [x]
26
-
27
-
28
21
  def tuples_to_lists(obj: Any) -> Any:
29
22
  """Recursively convert all tuples into lists so JMESPath indexers work reliably."""
30
23
  if isinstance(obj, tuple):
@@ -48,22 +41,13 @@ class ActionEngine:
48
41
  - handlers: a Handlers object (has get_handler)
49
42
  - special: a SpecialResolver (resolves $ref/$eval/... inside the actions spec, if you want)
50
43
  - substitutor: a TemplateSubstitutor (optional: expand ${...} in action specs)
51
- - resolve_special_in_actions: enable resolving special constructs inside the actions spec
52
- - substitute_templates_in_actions: enable template expansion inside the actions spec
53
-
54
- Notes on order:
55
- 1) Optionally substitute templates in 'actions' using 'source' as context
56
- 2) Optionally resolve special constructs in 'actions' using 'source' as context
57
- 3) Normalize to flat step list
58
- 4) Execute handlers
44
+ - normalizer: a Normalizer (for shorthand expansion)
59
45
  """
60
46
 
61
47
  handlers: Handlers = field(default_factory=Handlers)
62
48
  special: SpecialResolver = field(default_factory=SpecialResolver)
63
49
  substitutor: TemplateSubstitutor = field(default_factory=TemplateSubstitutor)
64
-
65
- resolve_special_in_actions: bool = True
66
- substitute_templates_in_actions: bool = True
50
+ normalizer: Normalizer = field(default_factory=Normalizer)
67
51
 
68
52
  def apply_actions(
69
53
  self,
@@ -107,46 +91,16 @@ class ActionEngine:
107
91
  out: List[dict] = []
108
92
  for item in spec:
109
93
  if isinstance(item, Mapping) and "op" not in item:
110
- out.extend(self._expand_shorthand(item))
94
+ out.extend(self.normalizer.expand_shorthand(item))
111
95
  else:
112
96
  out.append(item)
113
97
  return out
114
98
 
115
99
  if isinstance(spec, Mapping):
116
- return self._expand_shorthand(spec)
100
+ return self.normalizer.expand_shorthand(spec)
117
101
 
118
102
  raise TypeError("spec must be dict or list")
119
103
 
120
- @staticmethod
121
- def _expand_shorthand(obj: Mapping[str, Any]) -> List[dict]:
122
- """Expand shorthand mapping form into explicit op steps."""
123
- steps: List[dict] = []
124
-
125
- for key, val in obj.items():
126
- if key == "~delete":
127
- for p in _to_list(val):
128
- steps.append({"op": "delete", "path": p})
129
- continue
130
-
131
- if key == "~assert":
132
- if isinstance(val, Mapping):
133
- for p, eq in val.items():
134
- steps.append({"op": "assert", "path": p, "equals": eq})
135
- else:
136
- for p in _to_list(val):
137
- steps.append({"op": "assert", "path": p})
138
- continue
139
-
140
- append = isinstance(key, str) and key.endswith("[]")
141
- dst = f"{key[:-2]}/-" if append else key
142
-
143
- if _is_pointer_string(val):
144
- steps.append({"op": "copy", "from": val, "path": dst, "ignore_missing": True})
145
- else:
146
- steps.append({"op": "set", "path": dst, "value": val})
147
-
148
- return steps
149
-
150
104
 
151
105
  # Create a default engine instance for convenience
152
106
  default_engine = ActionEngine()
j_perm/funcs/subtract.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from jmespath import functions as _jp_funcs
4
4
 
5
- from j_perm.subst import JpFuncRegistry
5
+ from ..subst import JpFuncRegistry
6
6
 
7
7
 
8
8
  @JpFuncRegistry.register("subtract")
j_perm/normalizer.py ADDED
@@ -0,0 +1,88 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Callable, Mapping, Optional, List
5
+
6
+ ExpandResult = Optional[List[dict]]
7
+ Rule = Callable[[str, Any], ExpandResult]
8
+
9
+
10
+ class ShorthandRegistry:
11
+ _rules: dict[str, Rule] = {}
12
+ _default_priority: dict[str, int] = {} # name -> order (10, 5, 0, -5, -10...)
13
+
14
+ @classmethod
15
+ def register(cls, name: str, *, priority: int = 0):
16
+ def deco(fn: Rule) -> Rule:
17
+ if name in cls._rules:
18
+ raise ValueError(f"shorthand rule already registered: {name!r}")
19
+ cls._rules[name] = fn
20
+ cls._default_priority[name] = priority
21
+ return fn
22
+ return deco
23
+
24
+ @classmethod
25
+ def ordered(
26
+ cls,
27
+ rules: Mapping[str, Rule] | None = None,
28
+ *,
29
+ priority: Mapping[str, int] | None = None,
30
+ ) -> list[Rule]:
31
+ rules = dict(cls._rules if rules is None else rules)
32
+
33
+ _priority = dict(cls._default_priority)
34
+ if priority:
35
+ _priority.update(priority)
36
+
37
+ names = sorted(
38
+ rules.keys(),
39
+ key=lambda n: (_priority.get(n, 0), n),
40
+ reverse=True,
41
+ )
42
+ return [rules[n] for n in names]
43
+
44
+ @classmethod
45
+ def get(cls, name: str) -> Rule:
46
+ try:
47
+ return cls._rules[name]
48
+ except KeyError:
49
+ raise ValueError(f"Unknown rule {name!r}") from None
50
+
51
+ @classmethod
52
+ def all(cls) -> dict[str, Rule]:
53
+ return dict(cls._rules)
54
+
55
+
56
+ @dataclass(slots=True)
57
+ class Normalizer:
58
+ rules: list[str] | Mapping[str, Rule] | None = None
59
+ priority: Mapping[str, int] | None = None
60
+
61
+ _rules: list[Rule] = field(init=False)
62
+
63
+ def __post_init__(self) -> None:
64
+ if self.rules is None:
65
+ rules = ShorthandRegistry.all()
66
+ elif isinstance(self.rules, Mapping):
67
+ rules = dict(self.rules)
68
+ else:
69
+ all_rules = ShorthandRegistry.all()
70
+ rules = {}
71
+ for n in self.rules:
72
+ if n not in all_rules:
73
+ raise ValueError(f"Unknown rule {n!r}")
74
+ rules[n] = all_rules[n]
75
+
76
+ self._rules = ShorthandRegistry.ordered(rules, priority=self.priority)
77
+
78
+ def expand_shorthand(self, obj: Mapping[str, Any]) -> List[dict]:
79
+ steps: List[dict] = []
80
+ for k, v in obj.items():
81
+ for rule in self._rules:
82
+ out = rule(k, v)
83
+ if out is not None:
84
+ steps.extend(out)
85
+ break
86
+ else:
87
+ raise ValueError(f"Unhandled shorthand key: {k!r}")
88
+ return steps
j_perm/ops/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from ._assert import op_assert
2
+ from ._assert_d import op_assert_d
2
3
  from ._exec import op_exec
3
4
  from ._if import op_if
4
5
  from .copy import op_copy
j_perm/ops/_assert.py CHANGED
@@ -17,11 +17,11 @@ def op_assert(
17
17
  path = engine.substitutor.substitute(step["path"], src)
18
18
 
19
19
  try:
20
- current = jptr_get(dest, path)
20
+ current = jptr_get(src, path)
21
21
  except Exception:
22
- raise AssertionError(f"{path} does not exist")
22
+ raise AssertionError(f"'{path}' does not exist in source")
23
23
 
24
24
  if "equals" in step and current != step["equals"]:
25
- raise AssertionError(f"{path} != {step['equals']!r}")
25
+ raise AssertionError(f"'{path}' != '{step['equals']!r}'")
26
26
 
27
27
  return dest
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import MutableMapping, Any, Mapping
4
+
5
+ from ..op_handler import OpRegistry
6
+ from ..utils.pointers import jptr_get
7
+
8
+
9
+ @OpRegistry.register("assertD")
10
+ def op_assert_d(
11
+ step: dict,
12
+ dest: MutableMapping[str, Any],
13
+ src: Mapping[str, Any],
14
+ engine: "ActionEngine",
15
+ ) -> MutableMapping[str, Any]:
16
+ """Assert node existence and/or value at JSON Pointer path in dest."""
17
+ path = engine.substitutor.substitute(step["path"], dest)
18
+
19
+ try:
20
+ current = jptr_get(dest, path)
21
+ except Exception:
22
+ raise AssertionError(f"'{path}' does not exist in destination")
23
+
24
+ if "equals" in step and current != step["equals"]:
25
+ raise AssertionError(f"'{path}' != '{step['equals']!r}'")
26
+
27
+ return dest
j_perm/ops/_exec.py CHANGED
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  from typing import MutableMapping, Any, Mapping
4
4
 
5
5
  from ..op_handler import OpRegistry
6
- from ..engine import apply_actions
7
6
  from ..utils.pointers import maybe_slice
8
7
 
9
8
 
@@ -43,10 +42,10 @@ def op_exec(
43
42
  merge = bool(step.get("merge", False))
44
43
 
45
44
  if merge:
46
- result = apply_actions(actions, dest=dest, source=src)
45
+ result = engine.apply_actions(actions, dest=dest, source=src)
47
46
  return result
48
47
  else:
49
- result = apply_actions(actions, dest={}, source=src)
48
+ result = engine.apply_actions(actions, dest={}, source=src)
50
49
  dest.clear()
51
50
  if isinstance(dest, list):
52
51
  dest.extend(result) # type: ignore[arg-type]
@@ -0,0 +1,3 @@
1
+ from ._assert import rule_assert
2
+ from .assign_or_append import rule_assign_or_append
3
+ from .delete import rule_delete
@@ -0,0 +1,17 @@
1
+ from collections.abc import Mapping
2
+ from typing import Any
3
+
4
+ from ..normalizer import ShorthandRegistry, ExpandResult
5
+
6
+
7
+ @ShorthandRegistry.register("assert")
8
+ def rule_assert(key: str, val: Any) -> ExpandResult:
9
+ if key != "~assert" and key != "~assertD":
10
+ return None
11
+ if key == "~assertD":
12
+ op = "assertD"
13
+ else:
14
+ op = "assert"
15
+ if isinstance(val, Mapping):
16
+ return [{"op": op, "path": p, "equals": eq} for p, eq in val.items()]
17
+ return [{"op": op, "path": p} for p in (val if isinstance(val, list) else [val])]
@@ -0,0 +1,17 @@
1
+ from typing import Any
2
+
3
+ from ..normalizer import ExpandResult, ShorthandRegistry
4
+
5
+
6
+ @ShorthandRegistry.register("assign_or_append", priority=-1)
7
+ def rule_assign_or_append(key: str, val: Any) -> ExpandResult:
8
+ # Fallback rule: handles:
9
+ # - append shorthand: field[] -> "/field/-"
10
+ # - pointer assignment -> copy
11
+ # - literal assignment -> set
12
+ append = key.endswith("[]")
13
+ dst = f"{key[:-2]}/-" if append else key
14
+
15
+ if isinstance(val, str) and val.startswith("/"):
16
+ return [{"op": "copy", "from": val, "path": dst, "ignore_missing": True}]
17
+ return [{"op": "set", "path": dst, "value": val}]
@@ -0,0 +1,10 @@
1
+ from typing import Any
2
+
3
+ from ..normalizer import ShorthandRegistry, ExpandResult
4
+
5
+
6
+ @ShorthandRegistry.register("delete")
7
+ def rule_delete(key: str, val: Any) -> ExpandResult:
8
+ if key != "~delete":
9
+ return None
10
+ return [{"op": "delete", "path": p} for p in (val if isinstance(val, list) else [val])]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: j-perm
3
- Version: 0.2.0.1
3
+ Version: 0.2.1.1
4
4
  Summary: json permutation library
5
5
  Author-email: Roman <kuschanow@gmail.com>
6
6
  License: MIT
@@ -84,11 +84,13 @@ Typical setup:
84
84
  substitutor = TemplateSubstitutor()
85
85
  special = SpecialResolver()
86
86
  handlers = Handlers()
87
+ normalizer = Normalizer()
87
88
 
88
89
  engine = ActionEngine(
89
90
  handlers=handlers,
90
91
  special=special,
91
92
  substitutor=substitutor,
93
+ normalizer=normalizer,
92
94
  )
93
95
  ```
94
96
 
@@ -676,59 +678,103 @@ Update a mapping at `path` using either source mapping (`from`) or inline mappin
676
678
 
677
679
  ---
678
680
 
679
- ## Shorthand expansion (built-in)
681
+ ## Shorthand normalization
680
682
 
681
- Normalization expands shorthand syntax into explicit operation steps:
683
+ In addition to explicit DSL steps, J-Perm supports a *shorthand syntax* for more concise scripts.
684
+ Shorthand forms are expanded into regular operation steps **before execution**.
682
685
 
683
- ### `~delete`
686
+ Shorthand expansion is implemented as a pluggable normalization layer, similar to operations and special constructs.
687
+
688
+ ### How it works
689
+
690
+ During normalization, each mapping entry is processed by a chain of registered *shorthand rules*:
691
+
692
+ 1. Each rule decides whether it can handle a given `(key, value)` pair.
693
+ 2. If a rule matches, it expands the entry into one or more explicit operation steps.
694
+ 3. The first matching rule wins.
695
+ 4. If no rule matches, normalization fails with an error.
696
+
697
+ The resulting list of steps is then executed by the engine as a normal DSL script.
698
+
699
+ ---
700
+
701
+ ### Built-in shorthand rules
702
+
703
+ The following shorthand forms are enabled by default.
704
+
705
+ #### Delete shorthand (`~delete`)
684
706
 
685
707
  ```json
686
708
  { "~delete": ["/a", "/b"] }
687
709
  ```
688
710
 
689
-
711
+ Expands into:
690
712
 
691
713
  ```json
692
714
  { "op": "delete", "path": "/a" }
693
715
  { "op": "delete", "path": "/b" }
694
716
  ```
695
717
 
696
- ### `~assert`
718
+ ---
719
+
720
+ #### Assert shorthand (`~assert`)
721
+
722
+ Mapping form:
697
723
 
698
724
  ```json
699
- { "~assert": { "/x": 10 } }
725
+ { "~assert": { "/x": 10, "/y": 20 } }
700
726
  ```
701
727
 
702
-
728
+ Expands into:
703
729
 
704
730
  ```json
705
731
  { "op": "assert", "path": "/x", "equals": 10 }
732
+ { "op": "assert", "path": "/y", "equals": 20 }
706
733
  ```
707
734
 
708
- ### `field[]` append
735
+ List / string form:
736
+
737
+ ```json
738
+ { "~assert": ["/x", "/y"] }
739
+ ```
740
+
741
+ Expands into existence-only assertions.
742
+
743
+ ---
744
+
745
+ #### Append shorthand (`field[]`)
746
+
747
+ A key ending with `[]` means *append to a list* at that path:
709
748
 
710
749
  ```json
711
750
  { "items[]": 123 }
712
751
  ```
713
752
 
714
-
753
+ Expands into:
715
754
 
716
755
  ```json
717
756
  { "op": "set", "path": "/items/-", "value": 123 }
718
757
  ```
719
758
 
720
- ### pointer assignment
759
+ ---
760
+
761
+ #### Pointer assignment shorthand
721
762
 
722
- If a value is a string that starts with `/`, it becomes a `copy`:
763
+ If a value is a string that starts with `/`, it is treated as a source pointer:
723
764
 
724
765
  ```json
725
766
  { "name": "/user/fullName" }
726
767
  ```
727
768
 
728
-
769
+ Expands into:
729
770
 
730
771
  ```json
731
- { "op": "copy", "from": "/user/fullName", "path": "/name", "ignore_missing": true }
772
+ {
773
+ "op": "copy",
774
+ "from": "/user/fullName",
775
+ "path": "/name",
776
+ "ignore_missing": true
777
+ }
732
778
  ```
733
779
 
734
780
  ---
@@ -798,6 +844,21 @@ ${json:/raw_payload}
798
844
 
799
845
  ---
800
846
 
847
+ ### Custom shorthand rules
848
+
849
+ ```python
850
+ from j_perm import ShorthandRegistry, ExpandResult
851
+
852
+ @ShorthandRegistry.register("name", priority=10)
853
+ def my_shorthand_rule(key: str, value: Any) -> ExpandResult | None:
854
+ if key.startswith("my_prefix_"):
855
+ # expand into steps
856
+ steps = [ ... ]
857
+ return steps
858
+ ```
859
+
860
+ ---
861
+
801
862
  ## Plugin loading
802
863
 
803
864
  Registries collect definitions **at import time**.
@@ -1,5 +1,6 @@
1
- j_perm/__init__.py,sha256=RUpTLecqmkXgyy91s8DHNMEStW4almMYGHTJMmLR5NY,714
2
- j_perm/engine.py,sha256=xKSokxnkuVUyGZcsC1F88xzBw3vLpPPPUD1iHYKuKsI,5562
1
+ j_perm/__init__.py,sha256=81aLdcmlUg51R6xR-VXVDDUeM87ezy3ZuiLpblHZ_wk,873
2
+ j_perm/engine.py,sha256=iaUxB7EWcqvWsP4Z8z49a6-atX9br1tTC-WlZVrVo-A,3923
3
+ j_perm/normalizer.py,sha256=50pff5j3sTrCu2Fp_SW6m99qwt88CladpYlWpQ3c-ac,2671
3
4
  j_perm/op_handler.py,sha256=h28thz_18DwZTb9IZtTd6kfsZj7hN4UsmU4uf3aMYpo,1649
4
5
  j_perm/special_resolver.py,sha256=khRVWXrE3Vch1gbxsfFwj9JQ37pZ-_ojMeh-G2Vv7bs,4032
5
6
  j_perm/subst.py,sha256=oW21Gs2nTEQLcOC_xMKzeuZFtfNvnB2QZAIWl7U4TSg,9802
@@ -9,13 +10,14 @@ j_perm/casters/_float.py,sha256=C4r-h6zbkjmQnoEdE2RDMhSkW9oGrvxbQngJZ2T7_tU,153
9
10
  j_perm/casters/_int.py,sha256=f1BKqsULiG6Puov1qcGS_inSnv0hZtQ0deQEMP0v0i4,145
10
11
  j_perm/casters/_str.py,sha256=v1_BD1_hPbOtvO5CKYAlNEGbMhpR56eKCnV2a2Q4kGU,145
11
12
  j_perm/constructs/__init__.py,sha256=TTUPSMyvj8HSfPikrFqXaFsechc9aOIbjtVjy4TOolE,50
12
- j_perm/constructs/eval.py,sha256=ZckFMcerRWnu-51WAEdm35kuO7t_EzqvtiCAgVWDsjQ,457
13
- j_perm/constructs/ref.py,sha256=eUrRCanKAEWZ98jOAHNhjs5UhXNAm17NWd4Cbus_cyY,654
13
+ j_perm/constructs/eval.py,sha256=UzjZK27UAzsl3s26Y3RWBPhuKX1Az0fK6xk-cgpS8pQ,452
14
+ j_perm/constructs/ref.py,sha256=GtR2hpENev9k1lsx3mHGM8956vBfrPUgHm3IydYr-_I,644
14
15
  j_perm/funcs/__init__.py,sha256=8yIafxhCH5B7jXrsautQEtl4Qt8RcUK7IWNniORj9ow,31
15
- j_perm/funcs/subtract.py,sha256=0UW3McKL7VDPVj0UwfvZlrSXGs1uQ6yIdLz5rPqa7bY,293
16
- j_perm/ops/__init__.py,sha256=G4j6MGnEVnHSeJIBwrpV9oMk7s_djlEaOmLRW_4IEEo,329
17
- j_perm/ops/_assert.py,sha256=l07DjqsMbvoTDzLbVVcMciblYw8O4D6gBwPt3yFKPhs,758
18
- j_perm/ops/_exec.py,sha256=-esjfF2te1NvkclrJJ9lGU7YdwY_15vuxMihG08HhQM,1934
16
+ j_perm/funcs/subtract.py,sha256=8W49TsrnIU4EVjS8tWQY2GXlX2nAaQ1O9O1JlrVYdIw,288
17
+ j_perm/ops/__init__.py,sha256=j_U4Aq0iwEvZOF0jqMmlQA0NisJsHNJWzqkfyMWuHFA,364
18
+ j_perm/ops/_assert.py,sha256=O7MFxW3FDCglPMx-tBwi67k2Wd-Mds996AQdeGLbM6Q,773
19
+ j_perm/ops/_assert_d.py,sha256=xEjqBryCbzBpkzYhwRZht7-uyXv7XaCkEcRt6sjwE7k,783
20
+ j_perm/ops/_exec.py,sha256=HaFy__49QXnxYNbh1IdIu8ZQoWlQsrms5HgyRN5lV6o,1913
19
21
  j_perm/ops/_if.py,sha256=DNYp0j3-3YYMIR_CcDM3Ai0CpW2Qt4SYtKxk-9onu8c,1590
20
22
  j_perm/ops/copy.py,sha256=fV2rE92JyTRqo91GjXwWucklDR1T1ZcFHHbxG1F8S9g,1110
21
23
  j_perm/ops/copy_d.py,sha256=2SGHoq4vaD79diJos_oO3pvZvC_XrQorxhXAgZ5l09E,1017
@@ -26,9 +28,13 @@ j_perm/ops/replace_root.py,sha256=FFyc-95n3kTzcOXrnjwDdvwOnKNitXKb_1F25MMnXd8,44
26
28
  j_perm/ops/set.py,sha256=-kFFgHxeq65K5vLzDe6PhUoLT-ksoZYUVL1Jxf8bRA0,1882
27
29
  j_perm/ops/update.py,sha256=gvJTh3vS4zD-lpaqN23w5OO3hTtkSdu8RPy0V8r8Rwg,2471
28
30
  j_perm/schema/__init__.py,sha256=mhEbGdi1MgLeEm1IL8-tkTtbIdrdlvQi22sBr7vCUqg,3392
31
+ j_perm/shorthands/__init__.py,sha256=zvsu15iExYtUrct6Ob4IRs6Ao46PoZaCKVIUhMBY4Dk,117
32
+ j_perm/shorthands/_assert.py,sha256=iRvuUGlN5Bj9G5Bo-aXUa4MQujgmrR3N-PhDeee6xcY,555
33
+ j_perm/shorthands/assign_or_append.py,sha256=TsSzaQXb5qoKtY6tvx_skHob-695Zii5roUfXltznAQ,621
34
+ j_perm/shorthands/delete.py,sha256=PZeTRcP6-Q3SdbdyruWWXtMJ9mTnz1CBCHiOpHNI06Q,311
29
35
  j_perm/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
36
  j_perm/utils/pointers.py,sha256=X1HSCag4t-qq5zTvBMRAlBKXwpHVNNZYJrdJRtyEr4g,2788
31
- j_perm-0.2.0.1.dist-info/METADATA,sha256=5ptvJzqqqub2rmZpl33SR6Pv-IALMA9AFwlfnCEcNWw,16143
32
- j_perm-0.2.0.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
33
- j_perm-0.2.0.1.dist-info/top_level.txt,sha256=yveBxREqVn9DliNwmen1QWoxR0uta7bU5giS1CCffRI,7
34
- j_perm-0.2.0.1.dist-info/RECORD,,
37
+ j_perm-0.2.1.1.dist-info/METADATA,sha256=zwwrX08mxWxT5sMGLkh4PaLBVs7FMspDzwhcqj8sfpg,17660
38
+ j_perm-0.2.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
39
+ j_perm-0.2.1.1.dist-info/top_level.txt,sha256=yveBxREqVn9DliNwmen1QWoxR0uta7bU5giS1CCffRI,7
40
+ j_perm-0.2.1.1.dist-info/RECORD,,