j-perm 0.2.2__py3-none-any.whl → 0.2.4__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/constructs/ref.py CHANGED
@@ -9,7 +9,7 @@ from ..special_resolver import _MISSING, SpecialRegistry
9
9
  @SpecialRegistry.register("$ref")
10
10
  def of_ref(node: Mapping[str, Any], src: Mapping[str, Any], engine: "ActionEngine") -> Any:
11
11
  # Expand templates inside "$ref" using configured substitutor
12
- ptr = engine.substitutor.substitute(node["$ref"], src)
12
+ ptr = engine.substitutor.substitute(node["$ref"], src, engine)
13
13
 
14
14
  dflt = node.get("$default", _MISSING)
15
15
  try:
j_perm/engine.py CHANGED
@@ -51,6 +51,8 @@ class ActionEngine:
51
51
  normalizer: Normalizer = field(default_factory=Normalizer)
52
52
  pointer_manager: PointerManager = field(default_factory=PointerManager)
53
53
 
54
+ max_depth: int = 50
55
+
54
56
  def apply_actions(
55
57
  self,
56
58
  actions: Any,
@@ -111,7 +113,7 @@ default_engine = ActionEngine()
111
113
  def apply_actions(
112
114
  actions: Any,
113
115
  *,
114
- dest: JsonLikeDest,
116
+ dest: Dest,
115
117
  source: JsonLikeSource,
116
118
  ) -> Any:
117
119
  """Convenience function: apply a DSL script using the default engine."""
j_perm/ops/_assert.py CHANGED
@@ -13,7 +13,7 @@ def op_assert(
13
13
  engine: "ActionEngine",
14
14
  ) -> MutableMapping[str, Any]:
15
15
  """Assert node existence and/or value at JSON Pointer path in dest."""
16
- path = engine.substitutor.substitute(step["path"], src)
16
+ path = engine.substitutor.substitute(step["path"], src, engine)
17
17
 
18
18
  try:
19
19
  current = engine.pointer_manager.get_pointer(src, path)
j_perm/ops/_assert_d.py CHANGED
@@ -13,7 +13,7 @@ def op_assert_d(
13
13
  engine: "ActionEngine",
14
14
  ) -> MutableMapping[str, Any]:
15
15
  """Assert node existence and/or value at JSON Pointer path in dest."""
16
- path = engine.substitutor.substitute(step["path"], dest)
16
+ path = engine.substitutor.substitute(step["path"], dest, engine)
17
17
 
18
18
  try:
19
19
  current = engine.pointer_manager.get_pointer(dest, path)
j_perm/ops/_exec.py CHANGED
@@ -23,20 +23,20 @@ def op_exec(
23
23
  raise ValueError("exec operation requires either 'from' or 'actions' parameter")
24
24
 
25
25
  if has_from:
26
- actions_ptr = engine.substitutor.substitute(step["from"], src)
26
+ actions_ptr = engine.substitutor.substitute(step["from"], src, engine)
27
27
  try:
28
28
  actions = engine.pointer_manager.maybe_slice(actions_ptr, src)
29
29
  except Exception:
30
30
  if "default" in step:
31
31
  actions = engine.special.resolve(step["default"], src, engine)
32
32
  if isinstance(actions, (str, list, dict)):
33
- actions = engine.substitutor.substitute(actions, src)
33
+ actions = engine.substitutor.substitute(actions, src, engine)
34
34
  else:
35
35
  raise ValueError(f"Cannot find actions at {actions_ptr}")
36
36
  else:
37
37
  actions = engine.special.resolve(step["actions"], src, engine)
38
38
  if isinstance(actions, (str, list, dict)):
39
- actions = engine.substitutor.substitute(actions, src)
39
+ actions = engine.substitutor.substitute(actions, src, engine)
40
40
 
41
41
  merge = bool(step.get("merge", False))
42
42
 
j_perm/ops/_if.py CHANGED
@@ -16,7 +16,7 @@ def op_if(
16
16
  """Conditionally execute nested actions based on a path or expression."""
17
17
  if "path" in step:
18
18
  try:
19
- ptr = engine.substitutor.substitute(step["path"], src)
19
+ ptr = engine.substitutor.substitute(step["path"], src, engine)
20
20
  current = engine.pointer_manager.maybe_slice(ptr, dest)
21
21
  missing = False
22
22
  except Exception:
@@ -24,14 +24,14 @@ def op_if(
24
24
  missing = True
25
25
 
26
26
  if "equals" in step:
27
- expected = engine.substitutor.substitute(step["equals"], src)
27
+ expected = engine.substitutor.substitute(step["equals"], src, engine)
28
28
  cond_val = current == expected and not missing
29
29
  elif step.get("exists"):
30
30
  cond_val = not missing
31
31
  else:
32
32
  cond_val = bool(current) and not missing
33
33
  else:
34
- raw_cond = engine.substitutor.substitute(step.get("cond"), src)
34
+ raw_cond = engine.substitutor.substitute(step.get("cond"), src, engine)
35
35
  cond_val = bool(raw_cond)
36
36
 
37
37
  branch_key = "then" if cond_val else "else"
j_perm/ops/copy.py CHANGED
@@ -15,11 +15,11 @@ def op_copy(
15
15
  engine: "ActionEngine",
16
16
  ) -> MutableMapping[str, Any]:
17
17
  """Copy value from source pointer into dest path."""
18
- path = engine.substitutor.substitute(step["path"], src)
18
+ path = engine.substitutor.substitute(step["path"], src, engine)
19
19
  create = bool(step.get("create", True))
20
20
  extend_list = bool(step.get("extend", True))
21
21
 
22
- ptr = engine.substitutor.substitute(step["from"], src)
22
+ ptr = engine.substitutor.substitute(step["from"], src, engine)
23
23
  ignore = bool(step.get("ignore_missing", False))
24
24
 
25
25
  try:
@@ -36,4 +36,5 @@ def op_copy(
36
36
  {"op": "set", "path": path, "value": value, "create": create, "extend": extend_list},
37
37
  dest,
38
38
  src,
39
+ engine
39
40
  )
j_perm/ops/copy_d.py CHANGED
@@ -15,10 +15,10 @@ def op_copy_d(
15
15
  engine: "ActionEngine",
16
16
  ) -> MutableMapping[str, Any]:
17
17
  """Copy value from dest (self) into another dest path."""
18
- path = engine.substitutor.substitute(step["path"], src)
18
+ path = engine.substitutor.substitute(step["path"], src, engine)
19
19
  create = bool(step.get("create", True))
20
20
 
21
- ptr = engine.substitutor.substitute(step["from"], dest)
21
+ ptr = engine.substitutor.substitute(step["from"], dest, engine)
22
22
  ignore = bool(step.get("ignore_missing", False))
23
23
 
24
24
  try:
j_perm/ops/delete.py CHANGED
@@ -13,7 +13,7 @@ def op_delete(
13
13
  engine: "ActionEngine",
14
14
  ) -> MutableMapping[str, Any]:
15
15
  """Delete node at the given JSON Pointer path in dest."""
16
- path = engine.substitutor.substitute(step["path"], src)
16
+ path = engine.substitutor.substitute(step["path"], src, engine)
17
17
  ignore = bool(step.get("ignore_missing", True))
18
18
 
19
19
  try:
j_perm/ops/distinct.py CHANGED
@@ -13,14 +13,14 @@ def op_distinct(
13
13
  engine: "ActionEngine",
14
14
  ) -> MutableMapping[str, Any]:
15
15
  """Remove duplicates from a list at the given path, preserving order."""
16
- path = engine.substitutor.substitute(step["path"], src)
16
+ path = engine.substitutor.substitute(step["path"], src, engine)
17
17
  lst = engine.pointer_manager.get_pointer(dest, path)
18
18
 
19
19
  if not isinstance(lst, list):
20
20
  raise TypeError(f"{path} is not a list (distinct)")
21
21
 
22
22
  key = step.get("key", None)
23
- key_path = engine.substitutor.substitute(key, src)
23
+ key_path = engine.substitutor.substitute(key, src, engine)
24
24
 
25
25
  seen = set()
26
26
  unique = []
j_perm/ops/foreach.py CHANGED
@@ -14,7 +14,7 @@ def op_foreach(
14
14
  engine: "ActionEngine",
15
15
  ) -> MutableMapping[str, Any]:
16
16
  """Iterate over array in source and execute nested actions for each element."""
17
- arr_ptr = engine.substitutor.substitute(step["in"], src)
17
+ arr_ptr = engine.substitutor.substitute(step["in"], src, engine)
18
18
 
19
19
  default = copy.deepcopy(step.get("default", []))
20
20
  skip_empty = bool(step.get("skip_empty", True))
@@ -10,5 +10,5 @@ def op_replace_root(step, dest, src, engine):
10
10
  """Replace the whole dest root value with the resolved special value."""
11
11
  value = engine.special.resolve(step["value"], src, engine)
12
12
  if isinstance(value, (str, list, dict)):
13
- value = engine.substitutor.substitute(value, src)
13
+ value = engine.substitutor.substitute(value, src, engine)
14
14
  return copy.deepcopy(value)
j_perm/ops/set.py CHANGED
@@ -13,13 +13,13 @@ def op_set(
13
13
  engine: "ActionEngine",
14
14
  ) -> MutableMapping[str, Any]:
15
15
  """Set or append a value at JSON Pointer path in dest."""
16
- path = engine.substitutor.substitute(step["path"], src)
16
+ path = engine.substitutor.substitute(step["path"], src, engine)
17
17
  create = bool(step.get("create", True))
18
18
  extend_list = bool(step.get("extend", True))
19
19
 
20
20
  value = engine.special.resolve(step["value"], src, engine)
21
21
  if isinstance(value, (str, list, Mapping)):
22
- value = engine.substitutor.substitute(value, src)
22
+ value = engine.substitutor.substitute(value, src, engine)
23
23
 
24
24
  parent, leaf = engine.pointer_manager.ensure_parent(dest, path, create=create)
25
25
 
j_perm/ops/update.py CHANGED
@@ -14,12 +14,12 @@ def op_update(
14
14
  engine: "ActionEngine",
15
15
  ) -> MutableMapping[str, Any]:
16
16
  """Update a mapping at the given path using a mapping from source or inline value."""
17
- path = engine.substitutor.substitute(step["path"], src)
17
+ path = engine.substitutor.substitute(step["path"], src, engine)
18
18
  create = bool(step.get("create", True))
19
19
  deep = bool(step.get("deep", False))
20
20
 
21
21
  if "from" in step:
22
- ptr = engine.substitutor.substitute(step["from"], src)
22
+ ptr = engine.substitutor.substitute(step["from"], src, engine)
23
23
  try:
24
24
  update_value = copy.deepcopy(engine.pointer_manager.maybe_slice(ptr, src))
25
25
  except Exception:
@@ -30,7 +30,7 @@ def op_update(
30
30
  elif "value" in step:
31
31
  update_value = engine.special.resolve(step["value"], src, engine)
32
32
  if isinstance(update_value, (str, list, Mapping)):
33
- update_value = engine.substitutor.substitute(update_value, src)
33
+ update_value = engine.substitutor.substitute(update_value, src, engine)
34
34
  else:
35
35
  raise ValueError("update operation requires either 'from' or 'value' parameter")
36
36
 
j_perm/subst.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import copy
4
3
  import json
5
4
  from dataclasses import dataclass, field
6
5
  from typing import Any, Callable, Mapping
@@ -184,21 +183,30 @@ class TemplateSubstitutor:
184
183
  # Public API
185
184
  # -------------------------------------------------------------------------
186
185
 
187
- def substitute(self, obj: Any, data: Mapping[str, Any]) -> Any:
188
- return self.deep_substitute(obj, data)
186
+ def substitute(self, obj: Any, data: Mapping[str, Any], engine: "ActionEngine") -> Any:
187
+ res = self.deep_substitute(obj, data, engine)
188
+ return self._final_unescape(res)
189
189
 
190
- def flat_substitute(self, tmpl: str, data: Mapping[str, Any]) -> Any:
191
- if "${" not in tmpl:
190
+ def flat_substitute(self, tmpl: str, data: Mapping[str, Any], engine: "ActionEngine") -> Any:
191
+ if not self._has_unescaped_placeholder(tmpl):
192
192
  return tmpl
193
193
 
194
- if tmpl.startswith("${") and tmpl.endswith("}"):
195
- body = tmpl[2:-1]
196
- return copy.deepcopy(self._resolve_expr(body, data))
197
-
198
194
  out: list[str] = []
199
195
  i = 0
200
196
 
201
197
  while i < len(tmpl):
198
+ # literal "${"
199
+ if tmpl[i:i + 3] == "$${":
200
+ out.append("$${")
201
+ i += 3
202
+ continue
203
+
204
+ # literal "$"
205
+ if tmpl[i:i + 2] == "$$":
206
+ out.append("$$")
207
+ i += 2
208
+ continue
209
+
202
210
  if tmpl[i: i + 2] == "${":
203
211
  depth = 0
204
212
  j = i + 2
@@ -211,7 +219,7 @@ class TemplateSubstitutor:
211
219
  elif ch == "}":
212
220
  if depth == 0:
213
221
  expr = tmpl[i + 2: j]
214
- val = self._resolve_expr(expr, data)
222
+ val = self._resolve_expr(expr, data, engine)
215
223
 
216
224
  if isinstance(val, (Mapping, list)):
217
225
  rendered = json.dumps(val, ensure_ascii=False)
@@ -234,29 +242,29 @@ class TemplateSubstitutor:
234
242
 
235
243
  return "".join(out)
236
244
 
237
- def deep_substitute(self, obj: Any, data: Mapping[str, Any], _depth: int = 0) -> Any:
238
- if _depth > 50:
245
+ def deep_substitute(self, obj: Any, data: Mapping[str, Any], engine: "ActionEngine", _depth: int = 0) -> Any:
246
+ if _depth > engine.max_depth:
239
247
  raise RecursionError("too deep interpolation")
240
248
 
241
249
  if isinstance(obj, str):
242
- out = self.flat_substitute(obj, data)
243
- if isinstance(out, str) and "${" in out:
244
- return self.deep_substitute(out, data, _depth + 1)
250
+ out = self.flat_substitute(obj, data, engine)
251
+ if isinstance(out, str) and self._has_unescaped_placeholder(out):
252
+ return self.deep_substitute(out, data, engine, _depth + 1)
245
253
  return out
246
254
 
247
255
  if isinstance(obj, list):
248
- return [self.deep_substitute(item, data, _depth) for item in obj]
256
+ return [self.deep_substitute(item, data, engine, _depth) for item in obj]
249
257
 
250
258
  if isinstance(obj, tuple):
251
- return [self.deep_substitute(item, data, _depth) for item in obj]
259
+ return [self.deep_substitute(item, data, engine, _depth) for item in obj]
252
260
 
253
261
  if isinstance(obj, Mapping):
254
262
  out: dict[Any, Any] = {}
255
263
  for k, v in obj.items():
256
- new_key = self.deep_substitute(k, data, _depth) if isinstance(k, str) else k
264
+ new_key = self.deep_substitute(k, data, engine, _depth) if isinstance(k, str) else k
257
265
  if new_key in out:
258
266
  raise KeyError(f"duplicate key after substitution: {new_key!r}")
259
- out[new_key] = self.deep_substitute(v, data, _depth)
267
+ out[new_key] = self.deep_substitute(v, data, engine, _depth)
260
268
  return out
261
269
 
262
270
  return obj
@@ -265,7 +273,33 @@ class TemplateSubstitutor:
265
273
  # Internals
266
274
  # -------------------------------------------------------------------------
267
275
 
268
- def _resolve_expr(self, expr: str, data: Mapping[str, Any]) -> Any:
276
+ def _final_unescape(self, obj: Any) -> Any:
277
+ if isinstance(obj, str):
278
+ return obj.replace("$${", "${").replace("$$", "$")
279
+ if isinstance(obj, list):
280
+ return [self._final_unescape(x) for x in obj]
281
+ if isinstance(obj, tuple):
282
+ return tuple(self._final_unescape(x) for x in obj)
283
+ if isinstance(obj, Mapping):
284
+ return {self._final_unescape(k) if isinstance(k, str) else k: self._final_unescape(v)
285
+ for k, v in obj.items()}
286
+ return obj
287
+
288
+ @staticmethod
289
+ def _has_unescaped_placeholder(s: str) -> bool:
290
+ i = 0
291
+ while True:
292
+ j = s.find("${", i)
293
+ if j == -1:
294
+ return False
295
+ if j > 0 and s[j - 1] == "$":
296
+ i = j + 2
297
+ continue
298
+ return True
299
+
300
+ return False
301
+
302
+ def _resolve_expr(self, expr: str, data: Mapping[str, Any], engine: "ActionEngine") -> Any:
269
303
  expr = expr.strip()
270
304
 
271
305
  # 1) Casters
@@ -273,26 +307,26 @@ class TemplateSubstitutor:
273
307
  tag = f"{prefix}:"
274
308
  if expr.startswith(tag):
275
309
  inner = expr[len(tag):]
276
- value = self.flat_substitute(inner, data)
310
+ value = self.flat_substitute(inner, data, engine)
277
311
 
278
- if isinstance(value, str) and value.startswith("${") and value.endswith("}"):
279
- value = self.flat_substitute(value, data)
312
+ if isinstance(value, str) and self._has_unescaped_placeholder(value):
313
+ value = self.flat_substitute(value, data, engine)
280
314
 
281
315
  return fn(value)
282
316
 
283
317
  # 2) JMESPath
284
318
  if expr.startswith("?"):
285
319
  query_raw = expr[1:].lstrip()
286
- query_expanded = self.flat_substitute(query_raw, data)
320
+ query_expanded = self.flat_substitute(query_raw, data, engine)
287
321
  return jmespath.search(query_expanded, data, options=self._jp_options)
288
322
 
289
323
  # 3) Nested template
290
- if expr.startswith("${") and expr.endswith("}"):
291
- return self.flat_substitute(expr, data)
324
+ if self._has_unescaped_placeholder(expr):
325
+ return self.flat_substitute(expr, data, engine)
292
326
 
293
327
  # 4) JSON Pointer fallback
294
328
  pointer = "/" + expr.lstrip("/")
295
329
  try:
296
- return engine.pointer_manager.maybe_slice(pointer, data) # type: ignore[arg-type]
330
+ return engine.pointer_manager.maybe_slice(pointer, data)
297
331
  except Exception:
298
332
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: j-perm
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: json permutation library
5
5
  Author-email: Roman <kuschanow@gmail.com>
6
6
  License: MIT
@@ -799,8 +799,8 @@ def my_op(step, dest, src, engine):
799
799
  from j_perm import SpecialRegistry
800
800
 
801
801
  @SpecialRegistry.register("$upper")
802
- def sp_upper(node, src, resolver):
803
- value = resolver.substitutor.substitute(node["$upper"], src)
802
+ def sp_upper(node, src, resolver, engine):
803
+ value = resolver.substitutor.substitute(node["$upper"], src, engine)
804
804
  return str(value).upper()
805
805
  ```
806
806
 
@@ -1,10 +1,10 @@
1
1
  j_perm/__init__.py,sha256=81aLdcmlUg51R6xR-VXVDDUeM87ezy3ZuiLpblHZ_wk,873
2
- j_perm/engine.py,sha256=VFbzrywAucQ2VlIqSulstVe9zsEGRdHkZyhJEzWwPSw,4036
2
+ j_perm/engine.py,sha256=gHJdg-j_8UW9jmMQT6DIiNulJw5BQmSCEZihlGZsWJs,4053
3
3
  j_perm/normalizer.py,sha256=zxWtARRNuzS-zgFuB40dqU0oaUUk8PLKJNOPNb5wbRw,2672
4
4
  j_perm/op_handler.py,sha256=h28thz_18DwZTb9IZtTd6kfsZj7hN4UsmU4uf3aMYpo,1649
5
5
  j_perm/pointers.py,sha256=Czhq9vre_HnxhT1iOs8cDcKdCvEum5_YdvaEzoZ3iTY,3205
6
6
  j_perm/special_resolver.py,sha256=khRVWXrE3Vch1gbxsfFwj9JQ37pZ-_ojMeh-G2Vv7bs,4032
7
- j_perm/subst.py,sha256=aIkx5yi98tb2C5m1iOxvmLGiCYZXobgyT0-rsPssJ78,9778
7
+ j_perm/subst.py,sha256=I1O9qdmJp9TYjpfnqOgyIgL064hWGCmsGoxCbFWCBL4,11026
8
8
  j_perm/casters/__init__.py,sha256=mD49oMEyj6oDfSKXEJ8DbnmqkSypfEEg3VMI_rfaK-4,114
9
9
  j_perm/casters/_bool.py,sha256=qRsT7IcFBApD09elKUyUUaZmfePSIsOsKQHQHsgnHsw,230
10
10
  j_perm/casters/_float.py,sha256=C4r-h6zbkjmQnoEdE2RDMhSkW9oGrvxbQngJZ2T7_tU,153
@@ -12,28 +12,28 @@ j_perm/casters/_int.py,sha256=f1BKqsULiG6Puov1qcGS_inSnv0hZtQ0deQEMP0v0i4,145
12
12
  j_perm/casters/_str.py,sha256=v1_BD1_hPbOtvO5CKYAlNEGbMhpR56eKCnV2a2Q4kGU,145
13
13
  j_perm/constructs/__init__.py,sha256=TTUPSMyvj8HSfPikrFqXaFsechc9aOIbjtVjy4TOolE,50
14
14
  j_perm/constructs/eval.py,sha256=UzjZK27UAzsl3s26Y3RWBPhuKX1Az0fK6xk-cgpS8pQ,452
15
- j_perm/constructs/ref.py,sha256=qp1u7737LYDU-73DTP5fn-Q-X0kSNMlL3247vkYSTOQ,626
15
+ j_perm/constructs/ref.py,sha256=WgzEo54IhGWvBVSwPGTeqCRLDyK5uPJ01p5Wjd1YdNM,634
16
16
  j_perm/funcs/__init__.py,sha256=8yIafxhCH5B7jXrsautQEtl4Qt8RcUK7IWNniORj9ow,31
17
17
  j_perm/funcs/subtract.py,sha256=8W49TsrnIU4EVjS8tWQY2GXlX2nAaQ1O9O1JlrVYdIw,288
18
18
  j_perm/ops/__init__.py,sha256=j_U4Aq0iwEvZOF0jqMmlQA0NisJsHNJWzqkfyMWuHFA,364
19
- j_perm/ops/_assert.py,sha256=Z3FaNTHwwM8yey0cpbOrtJpQdBCsqASdU8KH2dymisU,761
20
- j_perm/ops/_assert_d.py,sha256=lPyvheXFjz955z-AvmZRCjcl7a8UGGZuNneiZp3yVIA,771
21
- j_perm/ops/_exec.py,sha256=_JhHyeFsRlFEwkrCipRyvzVLrScSHI595eW4lIrmNc0,1895
22
- j_perm/ops/_if.py,sha256=DJPrNVnxnUSfx8hcLtIaJP51MyK3zTOGtkVrbIFKlZE,1572
23
- j_perm/ops/copy.py,sha256=C3FUXxs1wBqMhu7NcpvxoBABMQK3DGfbaitqN7TYMqg,1092
24
- j_perm/ops/copy_d.py,sha256=ES5fYV-gbBZDK6kMew1jFb_lAkeUbpPAp2s3KoVBzqo,999
25
- j_perm/ops/delete.py,sha256=CuTHH1o7hWLbrdz6kvWFCWEirQEuKPpnkYtL-FytygI,886
26
- j_perm/ops/distinct.py,sha256=IK1edAwXMizLZ-3JvS2RvJ14DAJHR03Ji4Tzs0191Ao,1050
27
- j_perm/ops/foreach.py,sha256=E6YozlfwMi05yZI7dVTzy5Nh9QXfOpyRpfDB4wP4kYQ,1407
28
- j_perm/ops/replace_root.py,sha256=FFyc-95n3kTzcOXrnjwDdvwOnKNitXKb_1F25MMnXd8,445
29
- j_perm/ops/set.py,sha256=TfTkKNhPEpe58Vq8oftRTH23PEnRVpPF8QGemkUMi5E,1870
30
- j_perm/ops/update.py,sha256=rMeNYzjef_-d4uzO5PXHbpjMHcejNiNTEhLetxo3sxU,2433
19
+ j_perm/ops/_assert.py,sha256=vXsQBNIBQYLNSrO6u-vg4_oavvK5N_u-MAc8AqnISBk,769
20
+ j_perm/ops/_assert_d.py,sha256=sG1DC-ElJi54EN2LPzzbvw_5jtOoR1w6j_Ml3mO5Gwo,779
21
+ j_perm/ops/_exec.py,sha256=e3TCFN7bzJ_mjPbpm4ZZq0Dbz-YoJhOiHH1Dt4PwY3k,1919
22
+ j_perm/ops/_if.py,sha256=QziuiEOSkcOMMbyHm_C9IYRhybdtqiLdtQSBbyEL6To,1596
23
+ j_perm/ops/copy.py,sha256=485Gejp_qv3KN6SPu9vriSaJ7i1OxQj7McbQcNOPq20,1123
24
+ j_perm/ops/copy_d.py,sha256=iTlZ6lE1gMMcCaUjVWhbXHpBxd97OLXM9Ml5SP0Xvbw,1015
25
+ j_perm/ops/delete.py,sha256=YwPyMxPZ2LnCVC__bivvDLxlsvT2mnrmNIH7Qgw7bjk,894
26
+ j_perm/ops/distinct.py,sha256=eL7Ja4YNVCpsrgiuMxew2xqO3pLitJyAI3ygzy3hSVs,1066
27
+ j_perm/ops/foreach.py,sha256=zbELdguV5He_7nBoekaFsn6dsqBQuRzCoPIB44weUbE,1415
28
+ j_perm/ops/replace_root.py,sha256=klPY9E3KsPfnswIM4CdX_JPXGE1WWjr0sXSXSwn3ooo,453
29
+ j_perm/ops/set.py,sha256=YJjS1BXEEdPxFbUcJGu4YZPt0HHsk9cixpk0OnVQB4E,1886
30
+ j_perm/ops/update.py,sha256=9ouXm4ICNBqr03gJXUvV78AiytgkA4K2Fwb648r8rhE,2457
31
31
  j_perm/schema/__init__.py,sha256=mhEbGdi1MgLeEm1IL8-tkTtbIdrdlvQi22sBr7vCUqg,3392
32
32
  j_perm/shorthands/__init__.py,sha256=zvsu15iExYtUrct6Ob4IRs6Ao46PoZaCKVIUhMBY4Dk,117
33
33
  j_perm/shorthands/_assert.py,sha256=iRvuUGlN5Bj9G5Bo-aXUa4MQujgmrR3N-PhDeee6xcY,555
34
34
  j_perm/shorthands/assign_or_append.py,sha256=TsSzaQXb5qoKtY6tvx_skHob-695Zii5roUfXltznAQ,621
35
35
  j_perm/shorthands/delete.py,sha256=PZeTRcP6-Q3SdbdyruWWXtMJ9mTnz1CBCHiOpHNI06Q,311
36
- j_perm-0.2.2.dist-info/METADATA,sha256=0YW5JaEOhCYLcyVtDrHLdFCjzritqG13H1zoXvtdCr4,17658
37
- j_perm-0.2.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
38
- j_perm-0.2.2.dist-info/top_level.txt,sha256=yveBxREqVn9DliNwmen1QWoxR0uta7bU5giS1CCffRI,7
39
- j_perm-0.2.2.dist-info/RECORD,,
36
+ j_perm-0.2.4.dist-info/METADATA,sha256=ocLOygg0xjsrbelwpNrX0fMlDPT2He_Ur-NatWC0keM,17674
37
+ j_perm-0.2.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
38
+ j_perm-0.2.4.dist-info/top_level.txt,sha256=yveBxREqVn9DliNwmen1QWoxR0uta7bU5giS1CCffRI,7
39
+ j_perm-0.2.4.dist-info/RECORD,,
File without changes