dictwalk 0.1.0__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.
- dictwalk-0.1.0/.github/workflows/ci.yml +54 -0
- dictwalk-0.1.0/.github/workflows/release.yml +49 -0
- dictwalk-0.1.0/.gitignore +16 -0
- dictwalk-0.1.0/.python-version +1 -0
- dictwalk-0.1.0/.vscode/settings.json +8 -0
- dictwalk-0.1.0/Makefile +23 -0
- dictwalk-0.1.0/PKG-INFO +251 -0
- dictwalk-0.1.0/README.md +244 -0
- dictwalk-0.1.0/dictwalk/__init__.py +4 -0
- dictwalk-0.1.0/dictwalk/dictwalk.py +1481 -0
- dictwalk-0.1.0/dictwalk/errors.py +22 -0
- dictwalk-0.1.0/dictwalk/path_filters.py +144 -0
- dictwalk-0.1.0/examples.md +351 -0
- dictwalk-0.1.0/filters.md +126 -0
- dictwalk-0.1.0/pyproject.toml +29 -0
- dictwalk-0.1.0/tests/__init__.py +0 -0
- dictwalk-0.1.0/tests/unit/__init__.py +0 -0
- dictwalk-0.1.0/tests/unit/test_errors.py +52 -0
- dictwalk-0.1.0/tests/unit/test_get.py +383 -0
- dictwalk-0.1.0/tests/unit/test_set.py +274 -0
- dictwalk-0.1.0/tests/unit/test_unset.py +101 -0
- dictwalk-0.1.0/tox.ini +35 -0
- dictwalk-0.1.0/uv.lock +364 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
tox:
|
|
15
|
+
name: ${{ matrix.tox_env }}
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
strategy:
|
|
18
|
+
fail-fast: false
|
|
19
|
+
matrix:
|
|
20
|
+
include:
|
|
21
|
+
- tox_env: py310
|
|
22
|
+
python_version: "3.10"
|
|
23
|
+
- tox_env: py311
|
|
24
|
+
python_version: "3.11"
|
|
25
|
+
- tox_env: py312
|
|
26
|
+
python_version: "3.12"
|
|
27
|
+
- tox_env: py313
|
|
28
|
+
python_version: "3.13"
|
|
29
|
+
- tox_env: py314
|
|
30
|
+
python_version: "3.14"
|
|
31
|
+
- tox_env: lint
|
|
32
|
+
python_version: "3.13"
|
|
33
|
+
- tox_env: type
|
|
34
|
+
python_version: "3.13"
|
|
35
|
+
- tox_env: deptry
|
|
36
|
+
python_version: "3.13"
|
|
37
|
+
|
|
38
|
+
steps:
|
|
39
|
+
- name: Checkout
|
|
40
|
+
uses: actions/checkout@v4
|
|
41
|
+
|
|
42
|
+
- name: Set up Python
|
|
43
|
+
uses: actions/setup-python@v5
|
|
44
|
+
with:
|
|
45
|
+
python-version: ${{ matrix.python_version }}
|
|
46
|
+
allow-prereleases: true
|
|
47
|
+
|
|
48
|
+
- name: Set up uv
|
|
49
|
+
uses: astral-sh/setup-uv@v4
|
|
50
|
+
with:
|
|
51
|
+
enable-cache: true
|
|
52
|
+
|
|
53
|
+
- name: Run tox
|
|
54
|
+
run: uv run tox -e ${{ matrix.tox_env }}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: write
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
release:
|
|
11
|
+
if: github.ref == 'refs/heads/main'
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.13"
|
|
24
|
+
|
|
25
|
+
- name: Set up uv
|
|
26
|
+
uses: astral-sh/setup-uv@v4
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: true
|
|
29
|
+
|
|
30
|
+
- name: Resolve version
|
|
31
|
+
run: echo "VERSION=$(uv version --short)" >> "$GITHUB_ENV"
|
|
32
|
+
|
|
33
|
+
- name: Build distributions
|
|
34
|
+
run: uv build
|
|
35
|
+
|
|
36
|
+
- name: Publish to PyPI
|
|
37
|
+
env:
|
|
38
|
+
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
|
39
|
+
run: |
|
|
40
|
+
test -n "$UV_PUBLISH_TOKEN"
|
|
41
|
+
uv publish --token "$UV_PUBLISH_TOKEN"
|
|
42
|
+
|
|
43
|
+
- name: Tag release
|
|
44
|
+
run: |
|
|
45
|
+
test -n "$VERSION"
|
|
46
|
+
git config user.name "github-actions[bot]"
|
|
47
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
48
|
+
git tag "v$VERSION"
|
|
49
|
+
git push origin "v$VERSION"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
dictwalk-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.PHONY: test lint type deptry build release ci
|
|
2
|
+
|
|
3
|
+
test:
|
|
4
|
+
uv run tox -e py310,py311,py312,py313,py314
|
|
5
|
+
|
|
6
|
+
lint:
|
|
7
|
+
uv run tox -e lint
|
|
8
|
+
|
|
9
|
+
type:
|
|
10
|
+
uv run tox -e type
|
|
11
|
+
|
|
12
|
+
deptry:
|
|
13
|
+
uv run tox -e deptry
|
|
14
|
+
|
|
15
|
+
build:
|
|
16
|
+
uv build
|
|
17
|
+
|
|
18
|
+
release:
|
|
19
|
+
@test -n "$$PYPI_TOKEN" || (echo "PYPI_TOKEN is not set"; exit 1)
|
|
20
|
+
uv publish --token "$$PYPI_TOKEN"
|
|
21
|
+
|
|
22
|
+
ci:
|
|
23
|
+
uv run tox -e py310,py311,py312,py313,py314,lint,type,deptry
|
dictwalk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dictwalk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Dict path traversal and mutation utilities
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
|
|
8
|
+
# dictwalk
|
|
9
|
+
|
|
10
|
+
> This library is basically overengineered and ridiculous.
|
|
11
|
+
> It mostly exists because I kept asking an AI to add one more feature, then one more, then one more.
|
|
12
|
+
> Then I asked the AI to come up with features and I said screw it add them all.
|
|
13
|
+
> Please do not use this instead of just writing normal code.
|
|
14
|
+
> This is bored-developer scope creep in library form.
|
|
15
|
+
> The original idea was basically “jq/yq, but for Python dicts, and here we are”.
|
|
16
|
+
> I hope you get some use out of it, because I didn't.
|
|
17
|
+
|
|
18
|
+
`dictwalk` is a small utility for traversing and mutating nested Python dict/list data using path expressions.
|
|
19
|
+
|
|
20
|
+
It supports:
|
|
21
|
+
- Deep reads (`get`)
|
|
22
|
+
- Existence checks (`exists`)
|
|
23
|
+
- In-place writes (`set`)
|
|
24
|
+
- In-place removals (`unset`)
|
|
25
|
+
- Predicate filtering for lists
|
|
26
|
+
- Wildcards (`*`, `**`)
|
|
27
|
+
- Transform/filter pipelines (`|$filter`)
|
|
28
|
+
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
- Python `>=3.10`
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
From source:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install .
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
For local development:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
uv sync
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from dictwalk import dictwalk
|
|
51
|
+
|
|
52
|
+
data = {
|
|
53
|
+
"a": {
|
|
54
|
+
"users": [
|
|
55
|
+
{"id": 1, "name": "Ada", "active": True},
|
|
56
|
+
{"id": 2, "name": "Lin", "active": False},
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Read
|
|
62
|
+
names = dictwalk.get(data, "a.users[].name")
|
|
63
|
+
# ["Ada", "Lin"]
|
|
64
|
+
|
|
65
|
+
# Filter and map
|
|
66
|
+
active_names = dictwalk.get(data, "a.users[?active==True].name[]")
|
|
67
|
+
# ["Ada"]
|
|
68
|
+
|
|
69
|
+
# Write
|
|
70
|
+
dictwalk.set(data, "a.users[?id==2].active", True)
|
|
71
|
+
|
|
72
|
+
# Unset
|
|
73
|
+
dictwalk.unset(data, "a.users[?id==1].name")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Path Syntax
|
|
77
|
+
|
|
78
|
+
### Dot traversal
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
a.b.c
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Read nested object keys.
|
|
85
|
+
|
|
86
|
+
### List map
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
a.items[].id
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Apply the next token to every item in a list.
|
|
93
|
+
|
|
94
|
+
### List index and slice
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
a.items[0]
|
|
98
|
+
a.items[-1]
|
|
99
|
+
a.items[1:3]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Predicates
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
a.items[?id==1]
|
|
106
|
+
a.items[?score>=10]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
List predicates support:
|
|
110
|
+
- `==`, `!=`, `>`, `<`, `>=`, `<=`
|
|
111
|
+
|
|
112
|
+
### Predicate filters
|
|
113
|
+
|
|
114
|
+
Use registered filters on predicate values:
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
a.items[?id==$even]
|
|
118
|
+
a.items[?id==$gt(5)&&$lt(10)]
|
|
119
|
+
a.items[?id==!$odd]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Boolean operators in predicate filters:
|
|
123
|
+
- `&&` (and)
|
|
124
|
+
- `||` (or)
|
|
125
|
+
- `!` (not)
|
|
126
|
+
- parentheses for grouping
|
|
127
|
+
|
|
128
|
+
### Wildcards
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
a.*.id
|
|
132
|
+
a.**.id
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
- `*`: one level
|
|
136
|
+
- `**`: deep descendant traversal
|
|
137
|
+
|
|
138
|
+
### Output transforms
|
|
139
|
+
|
|
140
|
+
Apply filters to the final read value:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
a.value|$double|$string
|
|
144
|
+
a.list|$max
|
|
145
|
+
a.list|$double[]|$max
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## API
|
|
149
|
+
|
|
150
|
+
## `dictwalk.get(data, path, default=None, strict=False)`
|
|
151
|
+
|
|
152
|
+
- Returns resolved value.
|
|
153
|
+
- If `strict=False`: resolution failures return `default`.
|
|
154
|
+
- If `strict=True`: raises `DictWalkResolutionError`.
|
|
155
|
+
|
|
156
|
+
Special root token support in read paths:
|
|
157
|
+
|
|
158
|
+
```text
|
|
159
|
+
$$root.x
|
|
160
|
+
a.b.$$root.x
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## `dictwalk.exists(data, path, strict=False) -> bool`
|
|
164
|
+
|
|
165
|
+
- Returns `True` if path resolves, else `False`.
|
|
166
|
+
- If `strict=True`, raises `DictWalkResolutionError` on resolution failures.
|
|
167
|
+
|
|
168
|
+
## `dictwalk.set(data, path, value, *, strict=False, create_missing=True, create_filter_match=True, overwrite_incompatible=True) -> dict`
|
|
169
|
+
|
|
170
|
+
Mutates and returns the same `data` object.
|
|
171
|
+
|
|
172
|
+
`value` can be:
|
|
173
|
+
- A direct value (`42`, `"x"`, `{"k": 1}`)
|
|
174
|
+
- A filter string (`"$double"`, `"$add(2)|$string"`)
|
|
175
|
+
- A root reference expression:
|
|
176
|
+
- `$$root`
|
|
177
|
+
- `$$root.some.path`
|
|
178
|
+
- `$$root.some.path|$filter`
|
|
179
|
+
|
|
180
|
+
Notes:
|
|
181
|
+
- `$$root` is valid in `value`, not in write `path`.
|
|
182
|
+
- With `strict=True`, parent path must already resolve.
|
|
183
|
+
|
|
184
|
+
## `dictwalk.unset(data, path, *, strict=False) -> dict`
|
|
185
|
+
|
|
186
|
+
Removes targeted values in-place and returns the same object.
|
|
187
|
+
|
|
188
|
+
At terminal paths this can:
|
|
189
|
+
- Remove dict keys
|
|
190
|
+
- Remove list indexes/slices
|
|
191
|
+
- Remove list items matching a filter
|
|
192
|
+
|
|
193
|
+
## Filters
|
|
194
|
+
|
|
195
|
+
Built-in filters include arithmetic, string, collection, predicate, and datetime utilities.
|
|
196
|
+
|
|
197
|
+
Examples:
|
|
198
|
+
- Arithmetic: `$inc`, `$double`, `$add(2)`, `$round(1)`
|
|
199
|
+
- Collection: `$len`, `$max`, `$min`, `$sum`, `$avg`, `$unique`
|
|
200
|
+
- String: `$lower`, `$upper`, `$replace("a","b")`, `$split(",")`
|
|
201
|
+
- Predicates: `$even`, `$odd`, `$gt(10)`, `$contains("x")`
|
|
202
|
+
- Datetime: `$to_datetime`, `$timestamp`, `$age_seconds`, `$before(...)`, `$after(...)`
|
|
203
|
+
|
|
204
|
+
Register custom filters:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from dictwalk import dictwalk
|
|
208
|
+
|
|
209
|
+
dictwalk.register_path_filter("triple", lambda x: x * 3)
|
|
210
|
+
|
|
211
|
+
value = dictwalk.get({"a": {"b": 2}}, "a.b|$triple")
|
|
212
|
+
# 6
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Errors
|
|
216
|
+
|
|
217
|
+
From `dictwalk.errors`:
|
|
218
|
+
- `DictWalkError` (base)
|
|
219
|
+
- `DictWalkParseError`
|
|
220
|
+
- `DictWalkOperatorError`
|
|
221
|
+
- `DictWalkResolutionError`
|
|
222
|
+
|
|
223
|
+
Use `strict=True` when you want explicit failures instead of fallback defaults.
|
|
224
|
+
|
|
225
|
+
## Development
|
|
226
|
+
|
|
227
|
+
Run tests:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
make test
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Run lint/type/dependency checks:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
make lint
|
|
237
|
+
make type
|
|
238
|
+
make deptry
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Run everything:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
make ci
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Direct tox usage:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
uv run tox -e py310,py311,py312,py313,py314,lint,type,deptry
|
|
251
|
+
```
|
dictwalk-0.1.0/README.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# dictwalk
|
|
2
|
+
|
|
3
|
+
> This library is basically overengineered and ridiculous.
|
|
4
|
+
> It mostly exists because I kept asking an AI to add one more feature, then one more, then one more.
|
|
5
|
+
> Then I asked the AI to come up with features and I said screw it add them all.
|
|
6
|
+
> Please do not use this instead of just writing normal code.
|
|
7
|
+
> This is bored-developer scope creep in library form.
|
|
8
|
+
> The original idea was basically “jq/yq, but for Python dicts, and here we are”.
|
|
9
|
+
> I hope you get some use out of it, because I didn't.
|
|
10
|
+
|
|
11
|
+
`dictwalk` is a small utility for traversing and mutating nested Python dict/list data using path expressions.
|
|
12
|
+
|
|
13
|
+
It supports:
|
|
14
|
+
- Deep reads (`get`)
|
|
15
|
+
- Existence checks (`exists`)
|
|
16
|
+
- In-place writes (`set`)
|
|
17
|
+
- In-place removals (`unset`)
|
|
18
|
+
- Predicate filtering for lists
|
|
19
|
+
- Wildcards (`*`, `**`)
|
|
20
|
+
- Transform/filter pipelines (`|$filter`)
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- Python `>=3.10`
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
From source:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
For local development:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uv sync
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from dictwalk import dictwalk
|
|
44
|
+
|
|
45
|
+
data = {
|
|
46
|
+
"a": {
|
|
47
|
+
"users": [
|
|
48
|
+
{"id": 1, "name": "Ada", "active": True},
|
|
49
|
+
{"id": 2, "name": "Lin", "active": False},
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Read
|
|
55
|
+
names = dictwalk.get(data, "a.users[].name")
|
|
56
|
+
# ["Ada", "Lin"]
|
|
57
|
+
|
|
58
|
+
# Filter and map
|
|
59
|
+
active_names = dictwalk.get(data, "a.users[?active==True].name[]")
|
|
60
|
+
# ["Ada"]
|
|
61
|
+
|
|
62
|
+
# Write
|
|
63
|
+
dictwalk.set(data, "a.users[?id==2].active", True)
|
|
64
|
+
|
|
65
|
+
# Unset
|
|
66
|
+
dictwalk.unset(data, "a.users[?id==1].name")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Path Syntax
|
|
70
|
+
|
|
71
|
+
### Dot traversal
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
a.b.c
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Read nested object keys.
|
|
78
|
+
|
|
79
|
+
### List map
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
a.items[].id
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Apply the next token to every item in a list.
|
|
86
|
+
|
|
87
|
+
### List index and slice
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
a.items[0]
|
|
91
|
+
a.items[-1]
|
|
92
|
+
a.items[1:3]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Predicates
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
a.items[?id==1]
|
|
99
|
+
a.items[?score>=10]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
List predicates support:
|
|
103
|
+
- `==`, `!=`, `>`, `<`, `>=`, `<=`
|
|
104
|
+
|
|
105
|
+
### Predicate filters
|
|
106
|
+
|
|
107
|
+
Use registered filters on predicate values:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
a.items[?id==$even]
|
|
111
|
+
a.items[?id==$gt(5)&&$lt(10)]
|
|
112
|
+
a.items[?id==!$odd]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Boolean operators in predicate filters:
|
|
116
|
+
- `&&` (and)
|
|
117
|
+
- `||` (or)
|
|
118
|
+
- `!` (not)
|
|
119
|
+
- parentheses for grouping
|
|
120
|
+
|
|
121
|
+
### Wildcards
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
a.*.id
|
|
125
|
+
a.**.id
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- `*`: one level
|
|
129
|
+
- `**`: deep descendant traversal
|
|
130
|
+
|
|
131
|
+
### Output transforms
|
|
132
|
+
|
|
133
|
+
Apply filters to the final read value:
|
|
134
|
+
|
|
135
|
+
```text
|
|
136
|
+
a.value|$double|$string
|
|
137
|
+
a.list|$max
|
|
138
|
+
a.list|$double[]|$max
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## API
|
|
142
|
+
|
|
143
|
+
## `dictwalk.get(data, path, default=None, strict=False)`
|
|
144
|
+
|
|
145
|
+
- Returns resolved value.
|
|
146
|
+
- If `strict=False`: resolution failures return `default`.
|
|
147
|
+
- If `strict=True`: raises `DictWalkResolutionError`.
|
|
148
|
+
|
|
149
|
+
Special root token support in read paths:
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
$$root.x
|
|
153
|
+
a.b.$$root.x
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## `dictwalk.exists(data, path, strict=False) -> bool`
|
|
157
|
+
|
|
158
|
+
- Returns `True` if path resolves, else `False`.
|
|
159
|
+
- If `strict=True`, raises `DictWalkResolutionError` on resolution failures.
|
|
160
|
+
|
|
161
|
+
## `dictwalk.set(data, path, value, *, strict=False, create_missing=True, create_filter_match=True, overwrite_incompatible=True) -> dict`
|
|
162
|
+
|
|
163
|
+
Mutates and returns the same `data` object.
|
|
164
|
+
|
|
165
|
+
`value` can be:
|
|
166
|
+
- A direct value (`42`, `"x"`, `{"k": 1}`)
|
|
167
|
+
- A filter string (`"$double"`, `"$add(2)|$string"`)
|
|
168
|
+
- A root reference expression:
|
|
169
|
+
- `$$root`
|
|
170
|
+
- `$$root.some.path`
|
|
171
|
+
- `$$root.some.path|$filter`
|
|
172
|
+
|
|
173
|
+
Notes:
|
|
174
|
+
- `$$root` is valid in `value`, not in write `path`.
|
|
175
|
+
- With `strict=True`, parent path must already resolve.
|
|
176
|
+
|
|
177
|
+
## `dictwalk.unset(data, path, *, strict=False) -> dict`
|
|
178
|
+
|
|
179
|
+
Removes targeted values in-place and returns the same object.
|
|
180
|
+
|
|
181
|
+
At terminal paths this can:
|
|
182
|
+
- Remove dict keys
|
|
183
|
+
- Remove list indexes/slices
|
|
184
|
+
- Remove list items matching a filter
|
|
185
|
+
|
|
186
|
+
## Filters
|
|
187
|
+
|
|
188
|
+
Built-in filters include arithmetic, string, collection, predicate, and datetime utilities.
|
|
189
|
+
|
|
190
|
+
Examples:
|
|
191
|
+
- Arithmetic: `$inc`, `$double`, `$add(2)`, `$round(1)`
|
|
192
|
+
- Collection: `$len`, `$max`, `$min`, `$sum`, `$avg`, `$unique`
|
|
193
|
+
- String: `$lower`, `$upper`, `$replace("a","b")`, `$split(",")`
|
|
194
|
+
- Predicates: `$even`, `$odd`, `$gt(10)`, `$contains("x")`
|
|
195
|
+
- Datetime: `$to_datetime`, `$timestamp`, `$age_seconds`, `$before(...)`, `$after(...)`
|
|
196
|
+
|
|
197
|
+
Register custom filters:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from dictwalk import dictwalk
|
|
201
|
+
|
|
202
|
+
dictwalk.register_path_filter("triple", lambda x: x * 3)
|
|
203
|
+
|
|
204
|
+
value = dictwalk.get({"a": {"b": 2}}, "a.b|$triple")
|
|
205
|
+
# 6
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Errors
|
|
209
|
+
|
|
210
|
+
From `dictwalk.errors`:
|
|
211
|
+
- `DictWalkError` (base)
|
|
212
|
+
- `DictWalkParseError`
|
|
213
|
+
- `DictWalkOperatorError`
|
|
214
|
+
- `DictWalkResolutionError`
|
|
215
|
+
|
|
216
|
+
Use `strict=True` when you want explicit failures instead of fallback defaults.
|
|
217
|
+
|
|
218
|
+
## Development
|
|
219
|
+
|
|
220
|
+
Run tests:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
make test
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Run lint/type/dependency checks:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
make lint
|
|
230
|
+
make type
|
|
231
|
+
make deptry
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Run everything:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
make ci
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Direct tox usage:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
uv run tox -e py310,py311,py312,py313,py314,lint,type,deptry
|
|
244
|
+
```
|