dotted-notation 0.47.0__tar.gz → 0.47.1__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 (48) hide show
  1. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/CHANGELOG.md +7 -0
  2. {dotted_notation-0.47.0/dotted_notation.egg-info → dotted_notation-0.47.1}/PKG-INFO +41 -1
  3. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/README.md +40 -0
  4. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/transforms.py +18 -0
  5. {dotted_notation-0.47.0 → dotted_notation-0.47.1/dotted_notation.egg-info}/PKG-INFO +41 -1
  6. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/pyproject.toml +1 -1
  7. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/LICENSE +0 -0
  8. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/MANIFEST.in +0 -0
  9. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/__init__.py +0 -0
  10. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/__main__.py +0 -0
  11. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/access.pxd +0 -0
  12. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/access.py +0 -0
  13. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/api.py +0 -0
  14. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/base.pxd +0 -0
  15. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/base.py +0 -0
  16. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/cli/__init__.py +0 -0
  17. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/cli/_compat.py +0 -0
  18. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/cli/formats.py +0 -0
  19. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/cli/main.py +0 -0
  20. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/containers.py +0 -0
  21. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/engine.pxd +0 -0
  22. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/engine.py +0 -0
  23. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/filters.pxd +0 -0
  24. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/filters.py +0 -0
  25. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/grammar.py +0 -0
  26. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/groups.pxd +0 -0
  27. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/groups.py +0 -0
  28. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/matchers.pxd +0 -0
  29. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/matchers.py +0 -0
  30. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/native.py +0 -0
  31. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/predicates.py +0 -0
  32. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/recursive.pxd +0 -0
  33. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/recursive.py +0 -0
  34. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/results.py +0 -0
  35. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/sql/__init__.py +0 -0
  36. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/sql/core.py +0 -0
  37. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/sql/pg.py +0 -0
  38. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/utils.py +0 -0
  39. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/utypes.py +0 -0
  40. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/wrappers.pxd +0 -0
  41. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted/wrappers.py +0 -0
  42. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted_notation.egg-info/SOURCES.txt +0 -0
  43. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted_notation.egg-info/dependency_links.txt +0 -0
  44. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted_notation.egg-info/entry_points.txt +0 -0
  45. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted_notation.egg-info/requires.txt +0 -0
  46. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/dotted_notation.egg-info/top_level.txt +0 -0
  47. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/setup.cfg +0 -0
  48. {dotted_notation-0.47.0 → dotted_notation-0.47.1}/setup.py +0 -0
@@ -3,6 +3,13 @@
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.47.1]
7
+
8
+ ### Added
9
+ - The `coalesce` transform: `n|coalesce:0` is `n`, or 0 when `n` is
10
+ `None`. Several fallbacks take the first that is not `None`, so a
11
+ reference may stand in first: `n|coalesce:$$(defaults.n):0`.
12
+
6
13
  ## [0.47.0]
7
14
 
8
15
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dotted_notation
3
- Version: 0.47.0
3
+ Version: 0.47.1
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-Expression: MIT
@@ -273,6 +273,32 @@ Several Python libraries handle nested data access. Here's how dotted compares:
273
273
  <a id="breaking-changes"></a>
274
274
  ## Breaking Changes
275
275
 
276
+ ### v0.47.0
277
+ - **`!` binds tighter than guards, filters and transforms.** `!a=5` is now
278
+ `(!a)=5`, the values other than `a`'s that equal 5; the old meaning,
279
+ everything but an `a` equal to 5, is written `!(a=5)`. Likewise `!a&k=v`
280
+ filters what `!a` yields and `!a|int` transforms it. See
281
+ [Precedence](#precedence).
282
+ - **A guard, filter or transform after a group distributes to every branch.**
283
+ `(a,b)=5` is `(a=5,b=5)`, `(first&last)>5` is `(first>5&last>5)`,
284
+ `(a,b)|int` is `(a|int,b|int)`, and `assemble` writes the distributed form.
285
+ These used to fail to parse.
286
+ - **`a,b|int` transforms only `b`.** A transform binds to the expression on
287
+ its left before `,` and `&`; `(a,b)|int` transforms both.
288
+ - **`:type` tests the value a path yields, not the node it is applied to.**
289
+ `x:int` on `{'x': 3}` is `3`; it was `None`. Restrict a container one
290
+ segment earlier: `a.b:dict` → `a:dict.b`, and
291
+ `*[*]:!(str, bytes)` → `*:!(str, bytes)[*]`. `**:!(str, bytes)` and
292
+ `*(*, [*]):!(str, bytes)` now drop string values themselves, not only
293
+ their decomposition.
294
+ - **`!a.b` continues into `b`.** It yielded the complement of `a` and
295
+ ignored `.b`; it now yields `.b` of every key other than `a`, as
296
+ `match` always read it.
297
+ - **Ops own their transforms.** `Dotted.transforms` is read-only and
298
+ reflects the trailing op; `parse('a.b|int')` reprs as
299
+ `Dotted([a, b|int])`. A guard's or filter's match-only transforms,
300
+ `a|int=5`, are `guard_transforms`, not `transforms`.
301
+
276
302
  ### v0.44.0
277
303
  - **Parameter renamed from `key` to `path`** across the public API to align with
278
304
  the terminology used throughout the docs (a _path_ is the whole dotted
@@ -2958,6 +2984,7 @@ decorator.
2958
2984
  | `float` | `raises` | Convert to float |
2959
2985
  | `decimal` | `raises` | Convert to `Decimal` |
2960
2986
  | `none` | values... | Return `None` if falsy or matches values: `\|none::null:empty` |
2987
+ | `coalesce` | fallbacks... | The first of the value and the fallbacks that is not `None`: `\|coalesce:0` |
2961
2988
  | `strip` | `chars`, `raises` | Strip whitespace or specified chars |
2962
2989
  | `len` | `default` | Get length. Optional default if not sized: `\|len:0` |
2963
2990
  | `lowercase` | `raises` | Convert string to lowercase |
@@ -3012,6 +3039,19 @@ Comparison and membership transforms return `True`/`False`:
3012
3039
  >>> dotted.get({'n': 2}, 'n|in:[1, 2, 3]')
3013
3040
  True
3014
3041
 
3042
+ `coalesce` stands in a fallback for `None`, as SQL's does; with several
3043
+ fallbacks the first that is not `None` wins, which matters when a fallback is
3044
+ a [reference](#references-in-transforms-and-guards) that may itself be `None`:
3045
+
3046
+ >>> d = {'n': None, 'defaults': {'n': 7}}
3047
+ >>> dotted.get(d, 'n|coalesce:0')
3048
+ 0
3049
+ >>> dotted.get(d, 'n|coalesce:$$(defaults.n):0')
3050
+ 7
3051
+
3052
+ A transform only runs on a value the path yields, so `coalesce` does not fill
3053
+ a missing key; `get(..., default=)` does that.
3054
+
3015
3055
  <a id="container-transform-arguments"></a>
3016
3056
  ### Container transform arguments
3017
3057
 
@@ -226,6 +226,32 @@ Several Python libraries handle nested data access. Here's how dotted compares:
226
226
  <a id="breaking-changes"></a>
227
227
  ## Breaking Changes
228
228
 
229
+ ### v0.47.0
230
+ - **`!` binds tighter than guards, filters and transforms.** `!a=5` is now
231
+ `(!a)=5`, the values other than `a`'s that equal 5; the old meaning,
232
+ everything but an `a` equal to 5, is written `!(a=5)`. Likewise `!a&k=v`
233
+ filters what `!a` yields and `!a|int` transforms it. See
234
+ [Precedence](#precedence).
235
+ - **A guard, filter or transform after a group distributes to every branch.**
236
+ `(a,b)=5` is `(a=5,b=5)`, `(first&last)>5` is `(first>5&last>5)`,
237
+ `(a,b)|int` is `(a|int,b|int)`, and `assemble` writes the distributed form.
238
+ These used to fail to parse.
239
+ - **`a,b|int` transforms only `b`.** A transform binds to the expression on
240
+ its left before `,` and `&`; `(a,b)|int` transforms both.
241
+ - **`:type` tests the value a path yields, not the node it is applied to.**
242
+ `x:int` on `{'x': 3}` is `3`; it was `None`. Restrict a container one
243
+ segment earlier: `a.b:dict` → `a:dict.b`, and
244
+ `*[*]:!(str, bytes)` → `*:!(str, bytes)[*]`. `**:!(str, bytes)` and
245
+ `*(*, [*]):!(str, bytes)` now drop string values themselves, not only
246
+ their decomposition.
247
+ - **`!a.b` continues into `b`.** It yielded the complement of `a` and
248
+ ignored `.b`; it now yields `.b` of every key other than `a`, as
249
+ `match` always read it.
250
+ - **Ops own their transforms.** `Dotted.transforms` is read-only and
251
+ reflects the trailing op; `parse('a.b|int')` reprs as
252
+ `Dotted([a, b|int])`. A guard's or filter's match-only transforms,
253
+ `a|int=5`, are `guard_transforms`, not `transforms`.
254
+
229
255
  ### v0.44.0
230
256
  - **Parameter renamed from `key` to `path`** across the public API to align with
231
257
  the terminology used throughout the docs (a _path_ is the whole dotted
@@ -2911,6 +2937,7 @@ decorator.
2911
2937
  | `float` | `raises` | Convert to float |
2912
2938
  | `decimal` | `raises` | Convert to `Decimal` |
2913
2939
  | `none` | values... | Return `None` if falsy or matches values: `\|none::null:empty` |
2940
+ | `coalesce` | fallbacks... | The first of the value and the fallbacks that is not `None`: `\|coalesce:0` |
2914
2941
  | `strip` | `chars`, `raises` | Strip whitespace or specified chars |
2915
2942
  | `len` | `default` | Get length. Optional default if not sized: `\|len:0` |
2916
2943
  | `lowercase` | `raises` | Convert string to lowercase |
@@ -2965,6 +2992,19 @@ Comparison and membership transforms return `True`/`False`:
2965
2992
  >>> dotted.get({'n': 2}, 'n|in:[1, 2, 3]')
2966
2993
  True
2967
2994
 
2995
+ `coalesce` stands in a fallback for `None`, as SQL's does; with several
2996
+ fallbacks the first that is not `None` wins, which matters when a fallback is
2997
+ a [reference](#references-in-transforms-and-guards) that may itself be `None`:
2998
+
2999
+ >>> d = {'n': None, 'defaults': {'n': 7}}
3000
+ >>> dotted.get(d, 'n|coalesce:0')
3001
+ 0
3002
+ >>> dotted.get(d, 'n|coalesce:$$(defaults.n):0')
3003
+ 7
3004
+
3005
+ A transform only runs on a value the path yields, so `coalesce` does not fill
3006
+ a missing key; `get(..., default=)` does that.
3007
+
2968
3008
  <a id="container-transform-arguments"></a>
2969
3009
  ### Container transform arguments
2970
3010
 
@@ -104,6 +104,24 @@ def transform_none(val, *none_vals):
104
104
  return None if val in none_vals else val
105
105
 
106
106
 
107
+ @transform('coalesce')
108
+ def transform_coalesce(val, *fallbacks):
109
+ """
110
+ The first of val and the fallbacks that is not None
111
+ <dotted>|coalesce:<fallback> fallback if val is None else val
112
+ <dotted>|coalesce:<f1>:<f2> the first of val, f1, f2 not None
113
+ >>> import dotted
114
+ >>> dotted.get({'n': None}, 'n|coalesce:0')
115
+ 0
116
+ >>> dotted.get({'n': 5}, 'n|coalesce:0')
117
+ 5
118
+ """
119
+ for v in (val, *fallbacks):
120
+ if v is not None:
121
+ return v
122
+ return None
123
+
124
+
107
125
  @transform('strip')
108
126
  def transform_strip(val, chars=None, *modes):
109
127
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dotted_notation
3
- Version: 0.47.0
3
+ Version: 0.47.1
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-Expression: MIT
@@ -273,6 +273,32 @@ Several Python libraries handle nested data access. Here's how dotted compares:
273
273
  <a id="breaking-changes"></a>
274
274
  ## Breaking Changes
275
275
 
276
+ ### v0.47.0
277
+ - **`!` binds tighter than guards, filters and transforms.** `!a=5` is now
278
+ `(!a)=5`, the values other than `a`'s that equal 5; the old meaning,
279
+ everything but an `a` equal to 5, is written `!(a=5)`. Likewise `!a&k=v`
280
+ filters what `!a` yields and `!a|int` transforms it. See
281
+ [Precedence](#precedence).
282
+ - **A guard, filter or transform after a group distributes to every branch.**
283
+ `(a,b)=5` is `(a=5,b=5)`, `(first&last)>5` is `(first>5&last>5)`,
284
+ `(a,b)|int` is `(a|int,b|int)`, and `assemble` writes the distributed form.
285
+ These used to fail to parse.
286
+ - **`a,b|int` transforms only `b`.** A transform binds to the expression on
287
+ its left before `,` and `&`; `(a,b)|int` transforms both.
288
+ - **`:type` tests the value a path yields, not the node it is applied to.**
289
+ `x:int` on `{'x': 3}` is `3`; it was `None`. Restrict a container one
290
+ segment earlier: `a.b:dict` → `a:dict.b`, and
291
+ `*[*]:!(str, bytes)` → `*:!(str, bytes)[*]`. `**:!(str, bytes)` and
292
+ `*(*, [*]):!(str, bytes)` now drop string values themselves, not only
293
+ their decomposition.
294
+ - **`!a.b` continues into `b`.** It yielded the complement of `a` and
295
+ ignored `.b`; it now yields `.b` of every key other than `a`, as
296
+ `match` always read it.
297
+ - **Ops own their transforms.** `Dotted.transforms` is read-only and
298
+ reflects the trailing op; `parse('a.b|int')` reprs as
299
+ `Dotted([a, b|int])`. A guard's or filter's match-only transforms,
300
+ `a|int=5`, are `guard_transforms`, not `transforms`.
301
+
276
302
  ### v0.44.0
277
303
  - **Parameter renamed from `key` to `path`** across the public API to align with
278
304
  the terminology used throughout the docs (a _path_ is the whole dotted
@@ -2958,6 +2984,7 @@ decorator.
2958
2984
  | `float` | `raises` | Convert to float |
2959
2985
  | `decimal` | `raises` | Convert to `Decimal` |
2960
2986
  | `none` | values... | Return `None` if falsy or matches values: `\|none::null:empty` |
2987
+ | `coalesce` | fallbacks... | The first of the value and the fallbacks that is not `None`: `\|coalesce:0` |
2961
2988
  | `strip` | `chars`, `raises` | Strip whitespace or specified chars |
2962
2989
  | `len` | `default` | Get length. Optional default if not sized: `\|len:0` |
2963
2990
  | `lowercase` | `raises` | Convert string to lowercase |
@@ -3012,6 +3039,19 @@ Comparison and membership transforms return `True`/`False`:
3012
3039
  >>> dotted.get({'n': 2}, 'n|in:[1, 2, 3]')
3013
3040
  True
3014
3041
 
3042
+ `coalesce` stands in a fallback for `None`, as SQL's does; with several
3043
+ fallbacks the first that is not `None` wins, which matters when a fallback is
3044
+ a [reference](#references-in-transforms-and-guards) that may itself be `None`:
3045
+
3046
+ >>> d = {'n': None, 'defaults': {'n': 7}}
3047
+ >>> dotted.get(d, 'n|coalesce:0')
3048
+ 0
3049
+ >>> dotted.get(d, 'n|coalesce:$$(defaults.n):0')
3050
+ 7
3051
+
3052
+ A transform only runs on a value the path yields, so `coalesce` does not fill
3053
+ a missing key; `get(..., default=)` does that.
3054
+
3015
3055
  <a id="container-transform-arguments"></a>
3016
3056
  ### Container transform arguments
3017
3057
 
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "dotted_notation"
9
- version = "0.47.0"
9
+ version = "0.47.1"
10
10
  description = "Dotted notation for safe nested data traversal with optional chaining, pattern matching, and transforms"
11
11
  readme = "README.md"
12
12
  requires-python = ">=3.6"