Skip to content

Commit 4134599

Browse files
authored
Merge pull request #5 from maztohir/support-array-get
fix: support array on get and extract_dict
2 parents 4d0876c + 34347dc commit 4134599

File tree

7 files changed

+157
-30
lines changed

7 files changed

+157
-30
lines changed

.github/workflows/cd.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3+
4+
name: CD
5+
6+
on:
7+
push:
8+
branches:
9+
- main
10+
workflow_dispatch:
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
environment: production
17+
steps:
18+
- uses: actions/checkout@v3
19+
- name: Set up Python ${{ matrix.python-version }}
20+
uses: actions/setup-python@v3
21+
with:
22+
python-version: "3.9"
23+
- name: Install dependencies
24+
run: |
25+
python -m pip install --upgrade pip
26+
python -m pip install flake8 pytest
27+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
28+
- name: Lint with flake8
29+
run: |
30+
# stop the build if there are Python syntax errors or undefined names
31+
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
32+
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
33+
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
34+
- name: Test with pytest
35+
run: |
36+
pytest test*
37+
- name: Build dist
38+
if: github.ref == 'refs/heads/master'
39+
run: |
40+
python setup.py sdist bdist_wheel
41+
- name: Publish to PyPI
42+
if: github.ref == 'refs/heads/master'
43+
uses: pypa/gh-action-pypi-publish@release/v1
44+
with:
45+
user: __token__
46+
password: ${{ secrets.PYPI_API_TOKEN }}

.github/workflows/ci.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3+
4+
name: CI
5+
6+
on:
7+
pull_request:
8+
branches:
9+
- main
10+
workflow_dispatch:
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
python-version: ["3.9", "3.10", "3.11"]
20+
21+
steps:
22+
- uses: actions/checkout@v3
23+
- name: Set up Python ${{ matrix.python-version }}
24+
uses: actions/setup-python@v3
25+
with:
26+
python-version: ${{ matrix.python-version }}
27+
- name: Install dependencies
28+
run: |
29+
python -m pip install --upgrade pip
30+
python -m pip install flake8 pytest
31+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
32+
- name: Lint with flake8
33+
run: |
34+
# stop the build if there are Python syntax errors or undefined names
35+
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
36+
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
37+
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
38+
- name: Test with pytest
39+
run: |
40+
pytest test*

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ joe = DictPath(user, deepcopy=True)
7373

7474
#### Invalid path will return None
7575
```python
76-
from dict_path import extract_dict, inject_dict
76+
from dict_path import DictPath
7777

7878
test_dict = {'foo1':{'foo2':{'foo3':{'foo4':'bar'}}}}
7979
data = DictPath(test_dict)
@@ -82,14 +82,29 @@ data.get('foo1/foo2/foo3/foo4/foo6')
8282
```
8383
#### Set up unknown path will create an actual dict
8484
```python
85-
from dict_path import extract_dict, inject_dict
85+
from dict_path import DictPath
8686

8787
test_dict = {'foo1':{'foo2':{'foo3':{'foo4':'bar'}}}}
8888
data = DictPath(test_dict)
8989
data.set('foo1/foo2/foo3/foo5/foo6/foo7/foo8/', 'bar1')
9090
data.get('foo1/foo2/foo3/foo5/foo6/foo7/foo8/')
9191
#result: bar1
9292
```
93+
94+
#### Its also working with array
95+
Note: For now its only working with get or extract_dict.
96+
```python
97+
from dict_path import extract_dict, DictPath
98+
99+
test_dict = {'foo1':{'foo2':[{'foo3':'bar'},{'foo4':[[{'foo5': 'bar'}]]}]}}
100+
data = DictPath(test_dict)
101+
data.get('foo1/foo2/1/foo4/0/0/foo5/')
102+
#result: bar
103+
104+
extract_dict(test_dict, 'foo1/foo2/1/foo4/0/0/foo5/')
105+
```
106+
107+
93108
## Contributing
94109
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
95110

requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pytest==6.2.5
2+
twine==1.13.0
3+
wheel
4+
bump2version==1.0.1

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setuptools.setup(
77
name="dict-path",
8-
version="1.0.1",
8+
version="1.0.2",
99
author="Muhamad Tohir",
1010
author_email="maztohir@gmail.com",
1111
description="Python library to work with complicated nested dict",

src/dict_path/__init__.py

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,16 @@ def get(self, path: str) -> Union[DictPath, Any]:
6666
return self
6767
current = self.data
6868
for attr in path:
69-
if not isinstance(current, dict):
70-
raise Exception(f"Your path is not a path of dicts (value at key {attr} is of type {type(current)})")
71-
if attr not in current:
69+
if isinstance(current, dict):
70+
print(current, attr)
71+
current = current.get(attr)
72+
elif isinstance(current, list):
73+
print(current, attr)
74+
current = current[int(attr)]
75+
elif current is None:
7276
return None
73-
current = current[attr]
77+
else:
78+
raise Exception(f"Your path is not a path of dicts (value at key {attr} is of type {type(current)})")
7479
if isinstance(current, dict):
7580
return DictPath(current)
7681
return current
@@ -109,27 +114,30 @@ def __setitem__(self, path, value):
109114
self.set(path, value=value)
110115

111116
def extract_dict(dictionary, path):
112-
path = path[1:] if path.startswith('/') else path
113-
paths = path.split('/')
114-
active_dict = dictionary
115-
for p in paths:
116-
if active_dict.get(p) != None:
117-
active_dict = active_dict.get(p)
118-
else:
119-
return None
120-
return active_dict
117+
path = path[1:] if path.startswith('/') else path
118+
paths = path.split('/')
119+
active_dict = dictionary
120+
for p in paths:
121+
if active_dict is None:
122+
return None
123+
if isinstance(active_dict, dict):
124+
active_dict = active_dict.get(p)
125+
elif isinstance(active_dict, list):
126+
active_dict = active_dict[int(p)]
127+
return active_dict
121128

122129
def inject_dict(dictionary, path, value):
123-
path = path[1:] if path.startswith('/') else path
124-
paths = path.split('/')
125-
path_len = len(paths)
126-
_active_dict = dictionary
127-
for i, p in enumerate(paths):
128-
if i == path_len - 1:
129-
_active_dict[p] = value
130-
else:
131-
if _active_dict.get(p) == None:
132-
_active_dict[p] = {}
133-
_active_dict = _active_dict.get(p)
134-
return dictionary
135-
130+
path = path[1:] if path.startswith('/') else path
131+
paths = path.split('/')
132+
path_len = len(paths)
133+
_active_dict = dictionary
134+
for i, p in enumerate(paths):
135+
136+
if i == path_len - 1:
137+
_active_dict[p] = value
138+
continue
139+
140+
if _active_dict.get(p) == None:
141+
_active_dict[p] = {}
142+
_active_dict = _active_dict.get(p)
143+
return dictionary

test.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ def test_inject_value():
1313
inject_dict(test_dict, 'foo1/foo2/foo3', {'foo4':'bar'})
1414
assert extract_dict(test_dict, 'foo1/foo2/foo3/foo4') == 'bar'
1515

16+
def test_extract_array():
17+
test_dict = {'foo1':{'foo2':[{'foo3':'bar'},{'foo4':[[{'foo5': 'bar'}]]}]}}
18+
assert extract_dict(test_dict, 'foo1/foo2/1/foo4/0/0/foo5') == 'bar'
19+
1620
test_dict = {'foo1':{'foo2':{'foo3':{'foo4':'bar'}}}}
1721
data = DictPath(test_dict)
1822

@@ -48,4 +52,14 @@ def test_equalness_with_normal_dict():
4852
normal_dict = {'foo1':{'foo2':{'foo3':{'foo4':'bar'}}}}
4953
dic_path = DictPath(normal_dict)
5054
assert normal_dict == dic_path
51-
assert dic_path.dict is normal_dict
55+
assert dic_path.dict is normal_dict
56+
57+
def test_extract_array_with_dict():
58+
test_dict = {'foo1':{'foo2':[{'foo3':'bar'},{'foo4':[[{'foo5': 'bar'}]]}]}}
59+
dic_path = DictPath(test_dict)
60+
assert dic_path.get('foo1/foo2/1/foo4/0/0/foo5') == 'bar'
61+
62+
def test_extract_array_with_dict():
63+
test_dict = {'foo1':{'foo2':[{'foo3':'bar'},{'foo4':[[{'foo5': 'bar'}]]}]}}
64+
dic_path = DictPath(test_dict)
65+
assert dic_path.get('foo1/foo2/1/foo4/0/0/foo6/foo7') == None

0 commit comments

Comments
 (0)