dotted-notation 0.44.1__tar.gz → 0.44.2__tar.gz

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.
Files changed (38) hide show
  1. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/CHANGELOG.md +11 -0
  2. {dotted_notation-0.44.1/dotted_notation.egg-info → dotted_notation-0.44.2}/PKG-INFO +31 -1
  3. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/README.md +30 -0
  4. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/api.py +47 -11
  5. {dotted_notation-0.44.1 → dotted_notation-0.44.2/dotted_notation.egg-info}/PKG-INFO +31 -1
  6. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/pyproject.toml +1 -1
  7. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/LICENSE +0 -0
  8. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/MANIFEST.in +0 -0
  9. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/__init__.py +0 -0
  10. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/__main__.py +0 -0
  11. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/access.py +0 -0
  12. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/base.py +0 -0
  13. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/cli/__init__.py +0 -0
  14. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/cli/_compat.py +0 -0
  15. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/cli/formats.py +0 -0
  16. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/cli/main.py +0 -0
  17. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/containers.py +0 -0
  18. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/engine.py +0 -0
  19. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/filters.py +0 -0
  20. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/grammar.py +0 -0
  21. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/groups.py +0 -0
  22. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/matchers.py +0 -0
  23. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/predicates.py +0 -0
  24. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/recursive.py +0 -0
  25. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/results.py +0 -0
  26. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/sql/__init__.py +0 -0
  27. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/sql/core.py +0 -0
  28. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/sql/pg.py +0 -0
  29. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/transforms.py +0 -0
  30. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/utils.py +0 -0
  31. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/utypes.py +0 -0
  32. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted/wrappers.py +0 -0
  33. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted_notation.egg-info/SOURCES.txt +0 -0
  34. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted_notation.egg-info/dependency_links.txt +0 -0
  35. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted_notation.egg-info/entry_points.txt +0 -0
  36. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted_notation.egg-info/requires.txt +0 -0
  37. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/dotted_notation.egg-info/top_level.txt +0 -0
  38. {dotted_notation-0.44.1 → dotted_notation-0.44.2}/setup.cfg +0 -0
@@ -3,6 +3,17 @@
3
3
  All notable changes to `dotted` are recorded here. Versions prior to
4
4
  the ones listed are omitted — browse git history for earlier entries.
5
5
 
6
+ ## [0.44.2]
7
+
8
+ ### Added
9
+ - `unpack(obj, project=...)` keeps only the leaf paths selected by one or
10
+ more dotted patterns. Selection is directional (a leaf survives if it
11
+ `match`es a pattern), so projecting `a.b` never pulls in a shallower scalar
12
+ `a`. Matching defaults to `partial=True` (trailing segment is greedy); pass
13
+ `partial=False` for exact-depth matching, or override per field with a
14
+ `(pattern, partial)` tuple. `project=`/`partial=` also flow through `keys`,
15
+ `values`, and `items`.
16
+
6
17
  ## [0.44.1]
7
18
 
8
19
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dotted_notation
3
- Version: 0.44.1
3
+ Version: 0.44.2
4
4
  Summary: Dotted notation for safe nested data traversal with optional chaining, pattern matching, and transforms
5
5
  Author-email: Frey Waid <logophage1@gmail.com>
6
6
  License: MIT
@@ -729,6 +729,36 @@ For example:
729
729
 
730
730
  Pass both to include all attributes: `attrs=[Attrs.standard, Attrs.special]`.
731
731
 
732
+ Pass `project=` to keep only the leaf paths selected by one or more dotted
733
+ patterns. Selection is *directional* — a leaf survives if it `match`es a
734
+ projection pattern — so projecting `a.b` never drags in a shallower scalar leaf
735
+ `a`:
736
+
737
+ >>> o = {'a': {'b': 1, 'c': 2}, 'x': {'y': {'z': 3}}, 'extra': 9}
738
+ >>> dotted.unpack(o, project='a')
739
+ {'a.b': 1, 'a.c': 2}
740
+ >>> dotted.unpack(o, project=['a', 'x.y.z'])
741
+ {'a.b': 1, 'a.c': 2, 'x.y.z': 3}
742
+
743
+ Matching uses `match`'s `partial=True` by default, so a trailing segment is
744
+ greedy (`a.*` also keeps `a.b.c`). Set `partial=False` for exact-depth matching,
745
+ where `a.*`, `a.*.*`, and `a.**` are all distinct:
746
+
747
+ >>> deep = {'a': {'b': {'c': 1}}, 'd': 9}
748
+ >>> dotted.unpack(deep, project='a.*', partial=False)
749
+ {}
750
+ >>> dotted.unpack(deep, project='a.**', partial=False)
751
+ {'a.b.c': 1}
752
+
753
+ Override `partial` per field with a `(pattern, partial)` tuple — bare patterns
754
+ inherit the global setting. This matters for mid-pattern matches that `**`
755
+ cannot express (e.g. `a.*.c` keeping both `a.x.c` and `a.x.c.d`):
756
+
757
+ >>> dotted.unpack(deep, project=[('a.*', True), 'd'], partial=False)
758
+ {'a.b.c': 1, 'd': 9}
759
+
760
+ `project=` and `partial=` also flow through `keys()`, `values()`, and `items()`.
761
+
732
762
  <a id="pack"></a>
733
763
  ### Pack
734
764
 
@@ -689,6 +689,36 @@ For example:
689
689
 
690
690
  Pass both to include all attributes: `attrs=[Attrs.standard, Attrs.special]`.
691
691
 
692
+ Pass `project=` to keep only the leaf paths selected by one or more dotted
693
+ patterns. Selection is *directional* — a leaf survives if it `match`es a
694
+ projection pattern — so projecting `a.b` never drags in a shallower scalar leaf
695
+ `a`:
696
+
697
+ >>> o = {'a': {'b': 1, 'c': 2}, 'x': {'y': {'z': 3}}, 'extra': 9}
698
+ >>> dotted.unpack(o, project='a')
699
+ {'a.b': 1, 'a.c': 2}
700
+ >>> dotted.unpack(o, project=['a', 'x.y.z'])
701
+ {'a.b': 1, 'a.c': 2, 'x.y.z': 3}
702
+
703
+ Matching uses `match`'s `partial=True` by default, so a trailing segment is
704
+ greedy (`a.*` also keeps `a.b.c`). Set `partial=False` for exact-depth matching,
705
+ where `a.*`, `a.*.*`, and `a.**` are all distinct:
706
+
707
+ >>> deep = {'a': {'b': {'c': 1}}, 'd': 9}
708
+ >>> dotted.unpack(deep, project='a.*', partial=False)
709
+ {}
710
+ >>> dotted.unpack(deep, project='a.**', partial=False)
711
+ {'a.b.c': 1}
712
+
713
+ Override `partial` per field with a `(pattern, partial)` tuple — bare patterns
714
+ inherit the global setting. This matters for mid-pattern matches that `**`
715
+ cannot express (e.g. `a.*.c` keeping both `a.x.c` and `a.x.c.d`):
716
+
717
+ >>> dotted.unpack(deep, project=[('a.*', True), 'd'], partial=False)
718
+ {'a.b.c': 1, 'd': 9}
719
+
720
+ `project=` and `partial=` also flow through `keys()`, `values()`, and `items()`.
721
+
692
722
  <a id="pack"></a>
693
723
  ### Pack
694
724
 
@@ -1099,7 +1099,7 @@ def pack(pathvalues, apply_transforms=True, strict=False, bindings=None):
1099
1099
  return update_multi(AUTO, pathvalues, apply_transforms=apply_transforms, strict=strict, bindings=bindings)
1100
1100
 
1101
1101
 
1102
- def unpack(obj, attrs=None):
1102
+ def unpack(obj, attrs=None, project=None, partial=True):
1103
1103
  """
1104
1104
  Convert obj to dotted normal form. A dict mapping dotted paths to leaf
1105
1105
  values, which can be replayed to regenerate the obj (see `pack`).
@@ -1117,6 +1117,27 @@ def unpack(obj, attrs=None):
1117
1117
  {'a.b': [1, 2, 3], 'x.y.z': [4, 5], 'extra': 'stuff'}
1118
1118
  >>> pack(r) == d
1119
1119
  True
1120
+
1121
+ Pass project= to keep only the leaf paths selected by one or more dotted
1122
+ patterns. A leaf survives if it `match`es any projection pattern, so
1123
+ selection is directional: projecting 'a.b' never pulls in a scalar leaf
1124
+ 'a'. project= accepts a single pattern or an iterable of them:
1125
+
1126
+ >>> o = {'a': {'b': 1, 'c': 2}, 'x': {'y': {'z': 3}}, 'extra': 9}
1127
+ >>> unpack(o, project='a')
1128
+ {'a.b': 1, 'a.c': 2}
1129
+ >>> unpack(o, project=['a', 'x.y.z'])
1130
+ {'a.b': 1, 'a.c': 2, 'x.y.z': 3}
1131
+
1132
+ Matching uses match()'s `partial=True` by default, so a trailing segment
1133
+ is greedy ('a.*' also keeps 'a.b.c'). Set partial=False for exact-depth
1134
+ matching, or override it per-field with a (pattern, partial) tuple:
1135
+
1136
+ >>> deep = {'a': {'b': {'c': 1}}, 'd': 9}
1137
+ >>> unpack(deep, project='a.*', partial=False)
1138
+ {}
1139
+ >>> unpack(deep, project=[('a.*', True), 'd'])
1140
+ {'a.b.c': 1, 'd': 9}
1120
1141
  """
1121
1142
  # Accept either `Attrs` enum members or their string values.
1122
1143
  attr_values = {a.value if isinstance(a, Attrs) else a for a in (attrs or ())}
@@ -1128,43 +1149,58 @@ def unpack(obj, attrs=None):
1128
1149
  extra = ', @/(?!__).*/'
1129
1150
  else:
1130
1151
  extra = ', @/__.*/'
1131
- return dict(pluck(obj, f'*(*#, [*]:!(str, bytes){extra}):-2(.*, []{extra})##, (*, []{extra})'))
1152
+ result = dict(pluck(obj, f'*(*#, [*]:!(str, bytes){extra}):-2(.*, []{extra})##, (*, []{extra})'))
1153
+ if project is None:
1154
+ return result
1155
+ if isinstance(project, str):
1156
+ project = [project]
1157
+ # Normalize each entry to (pattern, partial); bare patterns inherit the
1158
+ # global `partial`, (pattern, partial) tuples override it per-field.
1159
+ specs = [(p, partial) if isinstance(p, str) else tuple(p) for p in project]
1160
+ return {k: v for k, v in result.items()
1161
+ if any(match(pat, k, partial=pp) for pat, pp in specs)}
1132
1162
 
1133
1163
 
1134
- def items(obj, attrs=None):
1164
+ def items(obj, attrs=None, project=None, partial=True):
1135
1165
  """
1136
1166
  Return (path, value) pairs of obj in normal form as a dict_items view.
1137
- Internally calls unpack().
1167
+ Internally calls unpack(); accepts project=/partial= (see unpack).
1138
1168
 
1139
1169
  >>> d = {'a': {'b': 1}, 'x': 2}
1140
1170
  >>> sorted(items(d))
1141
1171
  [('a.b', 1), ('x', 2)]
1172
+ >>> sorted(items(d, project='a'))
1173
+ [('a.b', 1)]
1142
1174
  """
1143
- return unpack(obj, attrs=attrs).items()
1175
+ return unpack(obj, attrs=attrs, project=project, partial=partial).items()
1144
1176
 
1145
1177
 
1146
- def keys(obj, attrs=None):
1178
+ def keys(obj, attrs=None, project=None, partial=True):
1147
1179
  """
1148
1180
  Return the dotted paths of obj in normal form as dict_keys.
1149
- Internally calls unpack().
1181
+ Internally calls unpack(); accepts project=/partial= (see unpack).
1150
1182
 
1151
1183
  >>> d = {'a': {'b': 1}, 'x': 2}
1152
1184
  >>> sorted(keys(d))
1153
1185
  ['a.b', 'x']
1186
+ >>> sorted(keys(d, project='a'))
1187
+ ['a.b']
1154
1188
  """
1155
- return unpack(obj, attrs=attrs).keys()
1189
+ return unpack(obj, attrs=attrs, project=project, partial=partial).keys()
1156
1190
 
1157
1191
 
1158
- def values(obj, attrs=None):
1192
+ def values(obj, attrs=None, project=None, partial=True):
1159
1193
  """
1160
1194
  Return the leaf values of obj in normal form.
1161
- Internally calls unpack().
1195
+ Internally calls unpack(); accepts project=/partial= (see unpack).
1162
1196
 
1163
1197
  >>> d = {'a': {'b': 1}, 'x': 2}
1164
1198
  >>> sorted(values(d))
1165
1199
  [1, 2]
1200
+ >>> sorted(values(d, project='a'))
1201
+ [1]
1166
1202
  """
1167
- return unpack(obj, attrs=attrs).values()
1203
+ return unpack(obj, attrs=attrs, project=project, partial=partial).values()
1168
1204
 
1169
1205
 
1170
1206
  #
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dotted_notation
3
- Version: 0.44.1
3
+ Version: 0.44.2
4
4
  Summary: Dotted notation for safe nested data traversal with optional chaining, pattern matching, and transforms
5
5
  Author-email: Frey Waid <logophage1@gmail.com>
6
6
  License: MIT
@@ -729,6 +729,36 @@ For example:
729
729
 
730
730
  Pass both to include all attributes: `attrs=[Attrs.standard, Attrs.special]`.
731
731
 
732
+ Pass `project=` to keep only the leaf paths selected by one or more dotted
733
+ patterns. Selection is *directional* — a leaf survives if it `match`es a
734
+ projection pattern — so projecting `a.b` never drags in a shallower scalar leaf
735
+ `a`:
736
+
737
+ >>> o = {'a': {'b': 1, 'c': 2}, 'x': {'y': {'z': 3}}, 'extra': 9}
738
+ >>> dotted.unpack(o, project='a')
739
+ {'a.b': 1, 'a.c': 2}
740
+ >>> dotted.unpack(o, project=['a', 'x.y.z'])
741
+ {'a.b': 1, 'a.c': 2, 'x.y.z': 3}
742
+
743
+ Matching uses `match`'s `partial=True` by default, so a trailing segment is
744
+ greedy (`a.*` also keeps `a.b.c`). Set `partial=False` for exact-depth matching,
745
+ where `a.*`, `a.*.*`, and `a.**` are all distinct:
746
+
747
+ >>> deep = {'a': {'b': {'c': 1}}, 'd': 9}
748
+ >>> dotted.unpack(deep, project='a.*', partial=False)
749
+ {}
750
+ >>> dotted.unpack(deep, project='a.**', partial=False)
751
+ {'a.b.c': 1}
752
+
753
+ Override `partial` per field with a `(pattern, partial)` tuple — bare patterns
754
+ inherit the global setting. This matters for mid-pattern matches that `**`
755
+ cannot express (e.g. `a.*.c` keeping both `a.x.c` and `a.x.c.d`):
756
+
757
+ >>> dotted.unpack(deep, project=[('a.*', True), 'd'], partial=False)
758
+ {'a.b.c': 1, 'd': 9}
759
+
760
+ `project=` and `partial=` also flow through `keys()`, `values()`, and `items()`.
761
+
732
762
  <a id="pack"></a>
733
763
  ### Pack
734
764
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dotted_notation"
7
- version = "0.44.1"
7
+ version = "0.44.2"
8
8
  description = "Dotted notation for safe nested data traversal with optional chaining, pattern matching, and transforms"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.6"