python-jsonstore 1.2.0__tar.gz → 1.3.3__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.
- python_jsonstore-1.3.3/.github/workflows/ci.yml +71 -0
- python_jsonstore-1.3.3/.github/workflows/publish.yml +27 -0
- python_jsonstore-1.3.3/PKG-INFO +152 -0
- {python-jsonstore-1.2.0 → python_jsonstore-1.3.3}/README.rst +30 -11
- {python-jsonstore-1.2.0 → python_jsonstore-1.3.3}/jsonstore.py +74 -51
- python_jsonstore-1.3.3/pyproject.toml +50 -0
- python_jsonstore-1.3.3/python_jsonstore.egg-info/PKG-INFO +152 -0
- {python-jsonstore-1.2.0 → python_jsonstore-1.3.3}/python_jsonstore.egg-info/SOURCES.txt +3 -3
- {python-jsonstore-1.2.0 → python_jsonstore-1.3.3}/setup.cfg +0 -3
- {python-jsonstore-1.2.0 → python_jsonstore-1.3.3}/test_jsonstore.py +50 -5
- python-jsonstore-1.2.0/.travis.yml +0 -26
- python-jsonstore-1.2.0/PKG-INFO +0 -126
- python-jsonstore-1.2.0/python_jsonstore.egg-info/PKG-INFO +0 -126
- python-jsonstore-1.2.0/setup.py +0 -46
- {python-jsonstore-1.2.0 → python_jsonstore-1.3.3}/LICENCE.md +0 -0
- {python-jsonstore-1.2.0 → python_jsonstore-1.3.3}/python_jsonstore.egg-info/dependency_links.txt +0 -0
- {python-jsonstore-1.2.0 → python_jsonstore-1.3.3}/python_jsonstore.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- master
|
|
8
|
+
pull_request:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
tests:
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
env:
|
|
14
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
os:
|
|
19
|
+
- ubuntu-latest
|
|
20
|
+
- windows-latest
|
|
21
|
+
python-version:
|
|
22
|
+
- "3.8"
|
|
23
|
+
- "3.9"
|
|
24
|
+
- "3.10"
|
|
25
|
+
- "3.11"
|
|
26
|
+
- "3.12"
|
|
27
|
+
- "3.13"
|
|
28
|
+
- "3.14"
|
|
29
|
+
- "pypy-3.9"
|
|
30
|
+
exclude:
|
|
31
|
+
- os: windows-latest
|
|
32
|
+
python-version: "pypy-3.9"
|
|
33
|
+
steps:
|
|
34
|
+
- name: Check out repository
|
|
35
|
+
uses: actions/checkout@v4
|
|
36
|
+
|
|
37
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
38
|
+
uses: actions/setup-python@v5
|
|
39
|
+
with:
|
|
40
|
+
python-version: ${{ matrix.python-version }}
|
|
41
|
+
|
|
42
|
+
- name: Install system dependencies
|
|
43
|
+
if: runner.os == 'Linux'
|
|
44
|
+
run: |
|
|
45
|
+
sudo apt-get update
|
|
46
|
+
sudo apt-get install -y zip p7zip-full
|
|
47
|
+
|
|
48
|
+
- name: Install Python dependencies
|
|
49
|
+
run: |
|
|
50
|
+
python -m pip install --upgrade pip
|
|
51
|
+
python -m pip install . pytest pytest-cov
|
|
52
|
+
|
|
53
|
+
- name: Run tests
|
|
54
|
+
run: pytest -vv --cov=jsonstore --cov-report=xml --junitxml=test-results.xml
|
|
55
|
+
|
|
56
|
+
- name: Upload coverage to Codecov
|
|
57
|
+
if: always() && env.CODECOV_TOKEN != ''
|
|
58
|
+
uses: codecov/codecov-action@v5
|
|
59
|
+
with:
|
|
60
|
+
token: ${{ env.CODECOV_TOKEN }}
|
|
61
|
+
files: ./coverage.xml
|
|
62
|
+
fail_ci_if_error: true
|
|
63
|
+
|
|
64
|
+
- name: Upload test results to Codecov
|
|
65
|
+
if: always() && env.CODECOV_TOKEN != ''
|
|
66
|
+
uses: codecov/codecov-action@v5
|
|
67
|
+
with:
|
|
68
|
+
token: ${{ env.CODECOV_TOKEN }}
|
|
69
|
+
files: ./test-results.xml
|
|
70
|
+
report_type: test_results
|
|
71
|
+
fail_ci_if_error: true
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build-and-publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
environment:
|
|
11
|
+
name: pypi
|
|
12
|
+
url: https://pypi.org/p/python-jsonstore
|
|
13
|
+
permissions:
|
|
14
|
+
id-token: write
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- name: Set up Python
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: '3.x'
|
|
21
|
+
- name: Build package
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip build
|
|
24
|
+
python -m build
|
|
25
|
+
# the publishing uses PyPI's trusted publishing, so configured on pypi instead of using secrets
|
|
26
|
+
- name: Publish to PyPI
|
|
27
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-jsonstore
|
|
3
|
+
Version: 1.3.3
|
|
4
|
+
Summary: Map JSON files onto Python attributes for simple configuration storage
|
|
5
|
+
Author-email: Oliver Bristow <github+pypi@oliverbristow.co.uk>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Code0x58/python-jsonstore/
|
|
8
|
+
Keywords: json,key,value,store
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 2
|
|
14
|
+
Classifier: Programming Language :: Python :: 2.7
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.4
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.5
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
28
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
29
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
30
|
+
Classifier: Topic :: Database
|
|
31
|
+
Classifier: Topic :: Software Development
|
|
32
|
+
Requires-Python: >=2.7
|
|
33
|
+
Description-Content-Type: text/x-rst
|
|
34
|
+
License-File: LICENCE.md
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
|PyPI| |Build Status| |Coverage Report|
|
|
38
|
+
|
|
39
|
+
jsonstore
|
|
40
|
+
=========
|
|
41
|
+
|
|
42
|
+
This module provides a class that maps keys and values from a JSON file
|
|
43
|
+
onto its attributes.
|
|
44
|
+
|
|
45
|
+
The goal was to provide a convenient way of loading and saving
|
|
46
|
+
configuration in a familiar human readable format. This is a bit more
|
|
47
|
+
flexible than the
|
|
48
|
+
`configparser <https://docs.python.org/3/library/configparser.html>`__
|
|
49
|
+
module which is included with Python.
|
|
50
|
+
|
|
51
|
+
This works is tested and working on Python 2.7 and Python 3.6+. It will
|
|
52
|
+
not work on 2.6 or lower. The tests are also executed on PyPy.
|
|
53
|
+
|
|
54
|
+
Examples
|
|
55
|
+
--------
|
|
56
|
+
|
|
57
|
+
Basics
|
|
58
|
+
~~~~~~
|
|
59
|
+
|
|
60
|
+
.. code:: python
|
|
61
|
+
|
|
62
|
+
# by default JsonStore commits on every change unless in a transaction
|
|
63
|
+
store = JsonStore('config.json')
|
|
64
|
+
store.a_string = "something"
|
|
65
|
+
store.a_list = [1, 2, 3]
|
|
66
|
+
store.a_dictionary = {
|
|
67
|
+
'dict-list': [{}],
|
|
68
|
+
'ln(2)': 0.69314718056,
|
|
69
|
+
'for-you': u"💐",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# you can use […] to set/get/delete string keys
|
|
73
|
+
store['some_key'] = "a value"
|
|
74
|
+
# the key is split on '.'s and works on dictionaries
|
|
75
|
+
del store['a_dictionary.dict-list']
|
|
76
|
+
store['a_dictionary.new_value'] = "old value"
|
|
77
|
+
# you can also use the syntactic sugar for tuple keys (explicit lists work too)
|
|
78
|
+
assert store['a_dictionary', 'new_value'] == "old value"
|
|
79
|
+
# you can traverse lists too
|
|
80
|
+
assert store['a_list', -1] == 3
|
|
81
|
+
# you can use slices in lists
|
|
82
|
+
assert len(store['a_list', 1:]) == 2
|
|
83
|
+
del store['a_list', :2]
|
|
84
|
+
assert store.a_list == [3]
|
|
85
|
+
|
|
86
|
+
# deep copies are made when assigning values
|
|
87
|
+
my_list = ['fun']
|
|
88
|
+
store.a_list = my_list
|
|
89
|
+
assert store.a_list is not my_list
|
|
90
|
+
assert 'a_list' in store
|
|
91
|
+
|
|
92
|
+
# deep copies are also returned to avoid unsanitary changes being made
|
|
93
|
+
store.a_dictionary['new_value'] = "new value" # won't update the store!
|
|
94
|
+
assert store.a_dictionary['new_value'] == "old value"
|
|
95
|
+
assert store.a_dictionary is not store.a_dictionary
|
|
96
|
+
|
|
97
|
+
# Appending to, extending a list
|
|
98
|
+
>>> store.list = [1, 2, 3]
|
|
99
|
+
|
|
100
|
+
# Because of the fact that .append won't modify the list in the actual file,
|
|
101
|
+
# but only a copy...
|
|
102
|
+
>>> store.list.append(4)
|
|
103
|
+
>>> store.list
|
|
104
|
+
[1, 2, 3]
|
|
105
|
+
|
|
106
|
+
# ... we need to rather use the += operator to append to a list.
|
|
107
|
+
>>> store.list += [4]
|
|
108
|
+
>>> store.list
|
|
109
|
+
[1, 2, 3, 4]
|
|
110
|
+
|
|
111
|
+
# Similarly, we can extend the list
|
|
112
|
+
>>> store.list += [5, 6]
|
|
113
|
+
>>> store.list
|
|
114
|
+
[1, 2, 3, 4, 5, 6]
|
|
115
|
+
|
|
116
|
+
Transactions
|
|
117
|
+
~~~~~~~~~~~~
|
|
118
|
+
|
|
119
|
+
``JsonStore`` objects can be used as `context
|
|
120
|
+
managers <https://www.python.org/dev/peps/pep-0343/>`__ to provide
|
|
121
|
+
transactions which are rolled back in the event of an exception. The
|
|
122
|
+
transaction model is primitive; you can only nest transactions.
|
|
123
|
+
|
|
124
|
+
While a store is put into a transaction, it will not save changes to
|
|
125
|
+
file until all of the transactions have been closed.
|
|
126
|
+
|
|
127
|
+
.. code:: python
|
|
128
|
+
|
|
129
|
+
from jsonstore import JsonStore
|
|
130
|
+
|
|
131
|
+
# even with auto_commit=True, the file won't be saved until the last contexts has been closed
|
|
132
|
+
with JsonStore('config.json', indent=None, auto_commit=False) as store:
|
|
133
|
+
self.value = 1
|
|
134
|
+
|
|
135
|
+
# the context manager will roll back changes made if an exception is raised
|
|
136
|
+
store = JsonStore('config.json', indent=None)
|
|
137
|
+
try:
|
|
138
|
+
with store:
|
|
139
|
+
store.value = "new"
|
|
140
|
+
raise Exception
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
# here we see the value that was saved previously
|
|
144
|
+
assert store.value == 1
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
.. |Build Status| image:: https://github.com/Code0x58/python-jsonstore/actions/workflows/ci.yml/badge.svg
|
|
148
|
+
:target: https://github.com/Code0x58/python-jsonstore/actions/workflows/ci.yml
|
|
149
|
+
.. |PyPI| image:: https://img.shields.io/pypi/v/python-jsonstore.svg
|
|
150
|
+
:target: https://pypi.org/project/python-jsonstore/
|
|
151
|
+
.. |Coverage Report| image:: https://codecov.io/gh/Code0x58/python-jsonstore/branch/master/graph/badge.svg
|
|
152
|
+
:target: https://codecov.io/gh/Code0x58/python-jsonstore
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
|
1
|
+
|PyPI| |Build Status| |Coverage Report|
|
|
2
2
|
|
|
3
3
|
jsonstore
|
|
4
4
|
=========
|
|
@@ -12,11 +12,8 @@ flexible than the
|
|
|
12
12
|
`configparser <https://docs.python.org/3/library/configparser.html>`__
|
|
13
13
|
module which is included with Python.
|
|
14
14
|
|
|
15
|
-
This works is tested and working on Python 2.7
|
|
16
|
-
not work on 2.6 or lower
|
|
17
|
-
do not work in 3.2.6 due to
|
|
18
|
-
`mistreating <https://travis-ci.org/Code0x58/python-jsonstore/jobs/198150401>`__
|
|
19
|
-
the 💩 when parsing the test code. This is also tested on pypy and pypy3.
|
|
15
|
+
This works is tested and working on Python 2.7 and Python 3.6+. It will
|
|
16
|
+
not work on 2.6 or lower. The tests are also executed on PyPy.
|
|
20
17
|
|
|
21
18
|
Examples
|
|
22
19
|
--------
|
|
@@ -47,6 +44,8 @@ Basics
|
|
|
47
44
|
assert store['a_list', -1] == 3
|
|
48
45
|
# you can use slices in lists
|
|
49
46
|
assert len(store['a_list', 1:]) == 2
|
|
47
|
+
del store['a_list', :2]
|
|
48
|
+
assert store.a_list == [3]
|
|
50
49
|
|
|
51
50
|
# deep copies are made when assigning values
|
|
52
51
|
my_list = ['fun']
|
|
@@ -59,6 +58,25 @@ Basics
|
|
|
59
58
|
assert store.a_dictionary['new_value'] == "old value"
|
|
60
59
|
assert store.a_dictionary is not store.a_dictionary
|
|
61
60
|
|
|
61
|
+
# Appending to, extending a list
|
|
62
|
+
>>> store.list = [1, 2, 3]
|
|
63
|
+
|
|
64
|
+
# Because of the fact that .append won't modify the list in the actual file,
|
|
65
|
+
# but only a copy...
|
|
66
|
+
>>> store.list.append(4)
|
|
67
|
+
>>> store.list
|
|
68
|
+
[1, 2, 3]
|
|
69
|
+
|
|
70
|
+
# ... we need to rather use the += operator to append to a list.
|
|
71
|
+
>>> store.list += [4]
|
|
72
|
+
>>> store.list
|
|
73
|
+
[1, 2, 3, 4]
|
|
74
|
+
|
|
75
|
+
# Similarly, we can extend the list
|
|
76
|
+
>>> store.list += [5, 6]
|
|
77
|
+
>>> store.list
|
|
78
|
+
[1, 2, 3, 4, 5, 6]
|
|
79
|
+
|
|
62
80
|
Transactions
|
|
63
81
|
~~~~~~~~~~~~
|
|
64
82
|
|
|
@@ -89,9 +107,10 @@ file until all of the transactions have been closed.
|
|
|
89
107
|
# here we see the value that was saved previously
|
|
90
108
|
assert store.value == 1
|
|
91
109
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
.. |PyPi Package| image:: https://badge.fury.io/py/python-jsonstore.svg
|
|
110
|
+
|
|
111
|
+
.. |Build Status| image:: https://github.com/Code0x58/python-jsonstore/actions/workflows/ci.yml/badge.svg
|
|
112
|
+
:target: https://github.com/Code0x58/python-jsonstore/actions/workflows/ci.yml
|
|
113
|
+
.. |PyPI| image:: https://img.shields.io/pypi/v/python-jsonstore.svg
|
|
97
114
|
:target: https://pypi.org/project/python-jsonstore/
|
|
115
|
+
.. |Coverage Report| image:: https://codecov.io/gh/Code0x58/python-jsonstore/branch/master/graph/badge.svg
|
|
116
|
+
:target: https://codecov.io/gh/Code0x58/python-jsonstore
|
|
@@ -12,6 +12,13 @@ from copy import deepcopy
|
|
|
12
12
|
|
|
13
13
|
__all__ = ["JsonStore"]
|
|
14
14
|
|
|
15
|
+
STRING_TYPES = (str,)
|
|
16
|
+
INT_TYPES = (int,)
|
|
17
|
+
if sys.version_info < (3,):
|
|
18
|
+
STRING_TYPES += (unicode,)
|
|
19
|
+
INT_TYPES += (long,)
|
|
20
|
+
VALUE_TYPES = (bool, int, float, type(None)) + INT_TYPES
|
|
21
|
+
|
|
15
22
|
|
|
16
23
|
class JsonStore(object):
|
|
17
24
|
"""A class to provide object based access to a JSON file"""
|
|
@@ -81,9 +88,10 @@ class JsonStore(object):
|
|
|
81
88
|
raise AttributeError(key)
|
|
82
89
|
|
|
83
90
|
@classmethod
|
|
84
|
-
def
|
|
91
|
+
def _verify_object(cls, obj, parents=None):
|
|
85
92
|
"""
|
|
86
|
-
|
|
93
|
+
Raise an exception if the object is not suitable for assignment.
|
|
94
|
+
|
|
87
95
|
"""
|
|
88
96
|
# pylint: disable=unicode-builtin,long-builtin
|
|
89
97
|
if isinstance(obj, (dict, list)):
|
|
@@ -94,85 +102,100 @@ class JsonStore(object):
|
|
|
94
102
|
parents.append(obj)
|
|
95
103
|
|
|
96
104
|
if isinstance(obj, dict):
|
|
97
|
-
|
|
98
|
-
cls._valid_string(k)
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
for k, v in obj.items():
|
|
106
|
+
if not cls._valid_string(k):
|
|
107
|
+
# this is necessary because of the JSON serialisation
|
|
108
|
+
raise TypeError("a dict has non-string keys")
|
|
109
|
+
cls._verify_object(v, parents)
|
|
101
110
|
elif isinstance(obj, (list, tuple)):
|
|
102
|
-
|
|
111
|
+
for o in obj:
|
|
112
|
+
cls._verify_object(o, parents)
|
|
103
113
|
else:
|
|
104
114
|
return cls._valid_value(obj)
|
|
105
115
|
|
|
106
116
|
@classmethod
|
|
107
117
|
def _valid_value(cls, value):
|
|
108
|
-
if isinstance(value,
|
|
109
|
-
return True
|
|
110
|
-
elif sys.version_info < (3,) and isinstance(value, long):
|
|
118
|
+
if isinstance(value, VALUE_TYPES):
|
|
111
119
|
return True
|
|
112
120
|
else:
|
|
113
121
|
return cls._valid_string(value)
|
|
114
122
|
|
|
115
123
|
@classmethod
|
|
116
124
|
def _valid_string(cls, value):
|
|
117
|
-
if isinstance(value,
|
|
125
|
+
if isinstance(value, STRING_TYPES):
|
|
118
126
|
return True
|
|
119
|
-
elif sys.version_info < (3,):
|
|
120
|
-
return isinstance(value, unicode)
|
|
121
127
|
else:
|
|
122
128
|
return False
|
|
123
129
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
130
|
+
@classmethod
|
|
131
|
+
def _canonical_key(cls, key):
|
|
132
|
+
"""Convert a set/get/del key into the canonical form."""
|
|
133
|
+
if cls._valid_string(key):
|
|
134
|
+
return tuple(key.split("."))
|
|
135
|
+
|
|
136
|
+
if isinstance(key, (tuple, list)):
|
|
137
|
+
key = tuple(key)
|
|
138
|
+
if not key:
|
|
139
|
+
raise TypeError("key must be a string or non-empty tuple/list")
|
|
140
|
+
return key
|
|
141
|
+
|
|
142
|
+
raise TypeError("key must be a string or non-empty tuple/list")
|
|
143
|
+
|
|
144
|
+
def __setattr__(self, attr, value):
|
|
145
|
+
self._verify_object(value)
|
|
146
|
+
self._data[attr] = deepcopy(value)
|
|
128
147
|
self._do_auto_commit()
|
|
129
148
|
|
|
130
|
-
def __delattr__(self,
|
|
131
|
-
del self._data[
|
|
149
|
+
def __delattr__(self, attr):
|
|
150
|
+
del self._data[attr]
|
|
132
151
|
|
|
133
|
-
def __get_obj(self,
|
|
134
|
-
"""
|
|
135
|
-
Returns the object which is under the given path
|
|
136
|
-
"""
|
|
137
|
-
if isinstance(full_path, (tuple, list)):
|
|
138
|
-
steps = full_path
|
|
139
|
-
else:
|
|
140
|
-
steps = full_path.split(".")
|
|
152
|
+
def __get_obj(self, steps):
|
|
153
|
+
"""Returns the object which is under the given path."""
|
|
141
154
|
path = []
|
|
142
155
|
obj = self._data
|
|
143
|
-
if not full_path:
|
|
144
|
-
return obj
|
|
145
156
|
for step in steps:
|
|
146
|
-
|
|
157
|
+
if isinstance(obj, dict) and not self._valid_string(step):
|
|
158
|
+
# this is necessary because of the JSON serialisation
|
|
159
|
+
raise TypeError("%s is a dict and %s is not a string" % (path, step))
|
|
147
160
|
try:
|
|
148
161
|
obj = obj[step]
|
|
149
|
-
except KeyError:
|
|
150
|
-
raise
|
|
162
|
+
except (KeyError, IndexError, TypeError) as e:
|
|
163
|
+
raise type(e)("unable to get %s from %s: %s" % (step, path, e))
|
|
164
|
+
path.append(step)
|
|
151
165
|
return obj
|
|
152
166
|
|
|
153
|
-
def __setitem__(self,
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
167
|
+
def __setitem__(self, key, value):
|
|
168
|
+
steps = self._canonical_key(key)
|
|
169
|
+
path, step = steps[:-1], steps[-1]
|
|
170
|
+
self._verify_object(value)
|
|
171
|
+
container = self.__get_obj(path)
|
|
172
|
+
if isinstance(container, dict) and not self._valid_string(step):
|
|
173
|
+
raise TypeError("%s is a dict and %s is not a string" % (path, step))
|
|
174
|
+
try:
|
|
175
|
+
container[step] = deepcopy(value)
|
|
176
|
+
except (IndexError, TypeError) as e:
|
|
177
|
+
raise type(e)("unable to set %s from %s: %s" % (step, path, e))
|
|
178
|
+
self._do_auto_commit()
|
|
161
179
|
|
|
162
180
|
def __getitem__(self, key):
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
raise KeyError
|
|
181
|
+
steps = self._canonical_key(key)
|
|
182
|
+
obj = self.__get_obj(steps)
|
|
166
183
|
return deepcopy(obj)
|
|
167
184
|
|
|
168
|
-
def __delitem__(self,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
key = name[-1]
|
|
172
|
-
else:
|
|
173
|
-
path, _, key = name.rpartition(".")
|
|
185
|
+
def __delitem__(self, key):
|
|
186
|
+
steps = self._canonical_key(key)
|
|
187
|
+
path, step = steps[:-1], steps[-1]
|
|
174
188
|
obj = self.__get_obj(path)
|
|
175
|
-
|
|
189
|
+
try:
|
|
190
|
+
del obj[step]
|
|
191
|
+
except (KeyError, IndexError, TypeError) as e:
|
|
192
|
+
raise type(e)("unable to delete %s from %s: %s" % (step, path, e))
|
|
176
193
|
|
|
177
194
|
def __contains__(self, key):
|
|
178
|
-
|
|
195
|
+
steps = self._canonical_key(key)
|
|
196
|
+
try:
|
|
197
|
+
self.__get_obj(steps)
|
|
198
|
+
return True
|
|
199
|
+
except (KeyError, IndexError, TypeError):
|
|
200
|
+
# this is rather permissive as the types are dynamic
|
|
201
|
+
return False
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=75", "setuptools_scm>=7", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "python-jsonstore"
|
|
7
|
+
description = "Map JSON files onto Python attributes for simple configuration storage"
|
|
8
|
+
readme = "README.rst"
|
|
9
|
+
requires-python = ">=2.7"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Oliver Bristow", email = "github+pypi@oliverbristow.co.uk"},
|
|
13
|
+
]
|
|
14
|
+
keywords = ["json", "key", "value", "store"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 5 - Production/Stable",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 2",
|
|
21
|
+
"Programming Language :: Python :: 2.7",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.3",
|
|
24
|
+
"Programming Language :: Python :: 3.4",
|
|
25
|
+
"Programming Language :: Python :: 3.5",
|
|
26
|
+
"Programming Language :: Python :: 3.6",
|
|
27
|
+
"Programming Language :: Python :: 3.7",
|
|
28
|
+
"Programming Language :: Python :: 3.8",
|
|
29
|
+
"Programming Language :: Python :: 3.9",
|
|
30
|
+
"Programming Language :: Python :: 3.10",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Programming Language :: Python :: 3.13",
|
|
34
|
+
"Programming Language :: Python :: 3.14",
|
|
35
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
36
|
+
"Programming Language :: Python :: Implementation :: PyPy",
|
|
37
|
+
"Topic :: Database",
|
|
38
|
+
"Topic :: Software Development",
|
|
39
|
+
]
|
|
40
|
+
dependencies = []
|
|
41
|
+
dynamic = ["version"]
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Homepage = "https://github.com/Code0x58/python-jsonstore/"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools]
|
|
47
|
+
py-modules = ["jsonstore"]
|
|
48
|
+
license-files = ["LICENCE.md"]
|
|
49
|
+
|
|
50
|
+
[tool.setuptools_scm]
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-jsonstore
|
|
3
|
+
Version: 1.3.3
|
|
4
|
+
Summary: Map JSON files onto Python attributes for simple configuration storage
|
|
5
|
+
Author-email: Oliver Bristow <github+pypi@oliverbristow.co.uk>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Code0x58/python-jsonstore/
|
|
8
|
+
Keywords: json,key,value,store
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 2
|
|
14
|
+
Classifier: Programming Language :: Python :: 2.7
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.4
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.5
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
28
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
29
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
30
|
+
Classifier: Topic :: Database
|
|
31
|
+
Classifier: Topic :: Software Development
|
|
32
|
+
Requires-Python: >=2.7
|
|
33
|
+
Description-Content-Type: text/x-rst
|
|
34
|
+
License-File: LICENCE.md
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
|PyPI| |Build Status| |Coverage Report|
|
|
38
|
+
|
|
39
|
+
jsonstore
|
|
40
|
+
=========
|
|
41
|
+
|
|
42
|
+
This module provides a class that maps keys and values from a JSON file
|
|
43
|
+
onto its attributes.
|
|
44
|
+
|
|
45
|
+
The goal was to provide a convenient way of loading and saving
|
|
46
|
+
configuration in a familiar human readable format. This is a bit more
|
|
47
|
+
flexible than the
|
|
48
|
+
`configparser <https://docs.python.org/3/library/configparser.html>`__
|
|
49
|
+
module which is included with Python.
|
|
50
|
+
|
|
51
|
+
This works is tested and working on Python 2.7 and Python 3.6+. It will
|
|
52
|
+
not work on 2.6 or lower. The tests are also executed on PyPy.
|
|
53
|
+
|
|
54
|
+
Examples
|
|
55
|
+
--------
|
|
56
|
+
|
|
57
|
+
Basics
|
|
58
|
+
~~~~~~
|
|
59
|
+
|
|
60
|
+
.. code:: python
|
|
61
|
+
|
|
62
|
+
# by default JsonStore commits on every change unless in a transaction
|
|
63
|
+
store = JsonStore('config.json')
|
|
64
|
+
store.a_string = "something"
|
|
65
|
+
store.a_list = [1, 2, 3]
|
|
66
|
+
store.a_dictionary = {
|
|
67
|
+
'dict-list': [{}],
|
|
68
|
+
'ln(2)': 0.69314718056,
|
|
69
|
+
'for-you': u"💐",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# you can use […] to set/get/delete string keys
|
|
73
|
+
store['some_key'] = "a value"
|
|
74
|
+
# the key is split on '.'s and works on dictionaries
|
|
75
|
+
del store['a_dictionary.dict-list']
|
|
76
|
+
store['a_dictionary.new_value'] = "old value"
|
|
77
|
+
# you can also use the syntactic sugar for tuple keys (explicit lists work too)
|
|
78
|
+
assert store['a_dictionary', 'new_value'] == "old value"
|
|
79
|
+
# you can traverse lists too
|
|
80
|
+
assert store['a_list', -1] == 3
|
|
81
|
+
# you can use slices in lists
|
|
82
|
+
assert len(store['a_list', 1:]) == 2
|
|
83
|
+
del store['a_list', :2]
|
|
84
|
+
assert store.a_list == [3]
|
|
85
|
+
|
|
86
|
+
# deep copies are made when assigning values
|
|
87
|
+
my_list = ['fun']
|
|
88
|
+
store.a_list = my_list
|
|
89
|
+
assert store.a_list is not my_list
|
|
90
|
+
assert 'a_list' in store
|
|
91
|
+
|
|
92
|
+
# deep copies are also returned to avoid unsanitary changes being made
|
|
93
|
+
store.a_dictionary['new_value'] = "new value" # won't update the store!
|
|
94
|
+
assert store.a_dictionary['new_value'] == "old value"
|
|
95
|
+
assert store.a_dictionary is not store.a_dictionary
|
|
96
|
+
|
|
97
|
+
# Appending to, extending a list
|
|
98
|
+
>>> store.list = [1, 2, 3]
|
|
99
|
+
|
|
100
|
+
# Because of the fact that .append won't modify the list in the actual file,
|
|
101
|
+
# but only a copy...
|
|
102
|
+
>>> store.list.append(4)
|
|
103
|
+
>>> store.list
|
|
104
|
+
[1, 2, 3]
|
|
105
|
+
|
|
106
|
+
# ... we need to rather use the += operator to append to a list.
|
|
107
|
+
>>> store.list += [4]
|
|
108
|
+
>>> store.list
|
|
109
|
+
[1, 2, 3, 4]
|
|
110
|
+
|
|
111
|
+
# Similarly, we can extend the list
|
|
112
|
+
>>> store.list += [5, 6]
|
|
113
|
+
>>> store.list
|
|
114
|
+
[1, 2, 3, 4, 5, 6]
|
|
115
|
+
|
|
116
|
+
Transactions
|
|
117
|
+
~~~~~~~~~~~~
|
|
118
|
+
|
|
119
|
+
``JsonStore`` objects can be used as `context
|
|
120
|
+
managers <https://www.python.org/dev/peps/pep-0343/>`__ to provide
|
|
121
|
+
transactions which are rolled back in the event of an exception. The
|
|
122
|
+
transaction model is primitive; you can only nest transactions.
|
|
123
|
+
|
|
124
|
+
While a store is put into a transaction, it will not save changes to
|
|
125
|
+
file until all of the transactions have been closed.
|
|
126
|
+
|
|
127
|
+
.. code:: python
|
|
128
|
+
|
|
129
|
+
from jsonstore import JsonStore
|
|
130
|
+
|
|
131
|
+
# even with auto_commit=True, the file won't be saved until the last contexts has been closed
|
|
132
|
+
with JsonStore('config.json', indent=None, auto_commit=False) as store:
|
|
133
|
+
self.value = 1
|
|
134
|
+
|
|
135
|
+
# the context manager will roll back changes made if an exception is raised
|
|
136
|
+
store = JsonStore('config.json', indent=None)
|
|
137
|
+
try:
|
|
138
|
+
with store:
|
|
139
|
+
store.value = "new"
|
|
140
|
+
raise Exception
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
# here we see the value that was saved previously
|
|
144
|
+
assert store.value == 1
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
.. |Build Status| image:: https://github.com/Code0x58/python-jsonstore/actions/workflows/ci.yml/badge.svg
|
|
148
|
+
:target: https://github.com/Code0x58/python-jsonstore/actions/workflows/ci.yml
|
|
149
|
+
.. |PyPI| image:: https://img.shields.io/pypi/v/python-jsonstore.svg
|
|
150
|
+
:target: https://pypi.org/project/python-jsonstore/
|
|
151
|
+
.. |Coverage Report| image:: https://codecov.io/gh/Code0x58/python-jsonstore/branch/master/graph/badge.svg
|
|
152
|
+
:target: https://codecov.io/gh/Code0x58/python-jsonstore
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
.travis.yml
|
|
2
1
|
LICENCE.md
|
|
3
2
|
README.rst
|
|
4
3
|
jsonstore.py
|
|
5
|
-
|
|
6
|
-
setup.py
|
|
4
|
+
pyproject.toml
|
|
7
5
|
test_jsonstore.py
|
|
6
|
+
.github/workflows/ci.yml
|
|
7
|
+
.github/workflows/publish.yml
|
|
8
8
|
python_jsonstore.egg-info/PKG-INFO
|
|
9
9
|
python_jsonstore.egg-info/SOURCES.txt
|
|
10
10
|
python_jsonstore.egg-info/dependency_links.txt
|
|
@@ -105,19 +105,39 @@ class Tests(unittest.TestCase):
|
|
|
105
105
|
self.assertRaises(KeyError, self._getitem(name))
|
|
106
106
|
self.assertRaises(AttributeError, self._getattr(name))
|
|
107
107
|
|
|
108
|
+
def test_slices(self):
|
|
109
|
+
self.store.list = [1, 2, 3]
|
|
110
|
+
self.store["list", :2] = ["a", "b"]
|
|
111
|
+
self.assertEqual(self.store["list", 1:], ["b", 3])
|
|
112
|
+
del self.store["list", :1]
|
|
113
|
+
self.assertEqual(self.store.list, ["b", 3])
|
|
114
|
+
|
|
108
115
|
def test_assign_invalid_types(self):
|
|
109
116
|
for method in (self._setattr, self._setitem):
|
|
110
117
|
|
|
111
118
|
def assign(value):
|
|
112
119
|
return method("key", value)
|
|
113
120
|
|
|
114
|
-
self.assertRaises(
|
|
115
|
-
self.assertRaises(
|
|
116
|
-
self.assertRaises(
|
|
121
|
+
self.assertRaises(TypeError, assign(set()))
|
|
122
|
+
self.assertRaises(TypeError, assign(object()))
|
|
123
|
+
self.assertRaises(TypeError, assign(None for i in range(2)))
|
|
124
|
+
self.assertRaises(TypeError, assign({1: 1}))
|
|
117
125
|
|
|
118
126
|
def test_assign_bad_keys(self):
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
value = 1
|
|
128
|
+
# the root object is a dict, so a string key is needed
|
|
129
|
+
self.assertRaises(TypeError, self._setitem(1, value))
|
|
130
|
+
self.assertRaises(TypeError, self._setitem((1, "a"), value))
|
|
131
|
+
|
|
132
|
+
self.store["dict"] = {}
|
|
133
|
+
self.store["list"] = []
|
|
134
|
+
self.assertRaises(TypeError, self._setitem((), value))
|
|
135
|
+
self.assertRaises(TypeError, self._setitem(("dict", 1), value))
|
|
136
|
+
self.assertRaises(TypeError, self._setitem(("dict", slice(1)), value))
|
|
137
|
+
self.assertRaises(TypeError, self._setitem(("list", "a"), value))
|
|
138
|
+
self.assertRaises(TypeError, self._setitem(("list", slice("a")), value))
|
|
139
|
+
self.assertRaises(IndexError, self._setitem(("list", 1), value))
|
|
140
|
+
|
|
121
141
|
|
|
122
142
|
def test_retrieve_values(self):
|
|
123
143
|
for name, value in self.TEST_DATA:
|
|
@@ -177,6 +197,7 @@ class Tests(unittest.TestCase):
|
|
|
177
197
|
assert self.store[["list", 0, "key", -1]] == "last"
|
|
178
198
|
self.assertRaises(TypeError, self._getitem("list.0.key.1"))
|
|
179
199
|
assert len(self.store["list", 0, "key", 1:]) == 2
|
|
200
|
+
self.assertRaises(IndexError, self._getitem(("list", 1)))
|
|
180
201
|
|
|
181
202
|
def test_del(self):
|
|
182
203
|
self.store.key = None
|
|
@@ -187,6 +208,18 @@ class Tests(unittest.TestCase):
|
|
|
187
208
|
del self.store["key"]
|
|
188
209
|
self.assertRaises(KeyError, self._getitem("key"))
|
|
189
210
|
|
|
211
|
+
with self.assertRaises(KeyError):
|
|
212
|
+
del self.store["missing"]
|
|
213
|
+
|
|
214
|
+
self.store.list = []
|
|
215
|
+
with self.assertRaises(IndexError):
|
|
216
|
+
del self.store["list", 1]
|
|
217
|
+
|
|
218
|
+
self.store.dict = {}
|
|
219
|
+
# somewhere after python 3.9 this went from a TypeError to a KeyError
|
|
220
|
+
with self.assertRaises((TypeError, KeyError)):
|
|
221
|
+
del self.store["dict", slice("a")]
|
|
222
|
+
|
|
190
223
|
def test_context_and_deserialisation(self):
|
|
191
224
|
store_file = mktemp()
|
|
192
225
|
for name, value in self.TEST_DATA:
|
|
@@ -284,6 +317,18 @@ class Tests(unittest.TestCase):
|
|
|
284
317
|
with open(self._store_file) as handle:
|
|
285
318
|
self.assertEqual(handle.read(), '{"value1": 1, "value2": 2}')
|
|
286
319
|
|
|
320
|
+
def test_list_concat_inplace(self):
|
|
321
|
+
self.store.list = []
|
|
322
|
+
extension = [{"key": "value"}]
|
|
323
|
+
|
|
324
|
+
# make sure += happens
|
|
325
|
+
self.store["list"] += extension
|
|
326
|
+
self.store.list += extension
|
|
327
|
+
self.assertEqual(self.store.list, extension * 2)
|
|
328
|
+
|
|
329
|
+
# make sure a deepcopy occurred
|
|
330
|
+
self.assertIsNot(self.store.list[0], extension[0])
|
|
331
|
+
|
|
287
332
|
|
|
288
333
|
if __name__ == "__main__":
|
|
289
334
|
unittest.main()
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
language: python
|
|
2
|
-
|
|
3
|
-
jobs:
|
|
4
|
-
include:
|
|
5
|
-
- python: 3.8
|
|
6
|
-
- python: 3.7
|
|
7
|
-
- python: 3.6
|
|
8
|
-
- python: 3.5
|
|
9
|
-
- python: 3.4
|
|
10
|
-
- python: 2.7
|
|
11
|
-
- python: pypy3
|
|
12
|
-
- python: pypy
|
|
13
|
-
- name: "Python: 3.7"
|
|
14
|
-
os: osx
|
|
15
|
-
osx_image: xcode11.2 # Python 3.7.4 running on macOS 10.14.4
|
|
16
|
-
language: shell # 'language: python' is an error on Travis CI macOS
|
|
17
|
-
- name: "Python: 3.8"
|
|
18
|
-
os: windows # Windows 10.0.17134 N/A Build 17134
|
|
19
|
-
language: shell # 'language: python' is an error on Travis CI Windows
|
|
20
|
-
before_install:
|
|
21
|
-
- choco install python --version 3.8.2
|
|
22
|
-
- python -m pip install --upgrade pip
|
|
23
|
-
env: PATH=/c/Python38:/c/Python38/Scripts:$PATH
|
|
24
|
-
|
|
25
|
-
install: python -m pip install . pytest
|
|
26
|
-
script: python -m pytest
|
python-jsonstore-1.2.0/PKG-INFO
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: python-jsonstore
|
|
3
|
-
Version: 1.2.0
|
|
4
|
-
Summary: UNKNOWN
|
|
5
|
-
Home-page: https://github.com/Code0x58/python-jsonstore/
|
|
6
|
-
Author: Oliver Bristow
|
|
7
|
-
Author-email: github+pypi@oliverbristow.co.uk
|
|
8
|
-
License: MIT
|
|
9
|
-
Description: |PyPi Package| |Build Status| |Codacy Rating|
|
|
10
|
-
|
|
11
|
-
jsonstore
|
|
12
|
-
=========
|
|
13
|
-
|
|
14
|
-
This module provides a class that maps keys and values from a JSON file
|
|
15
|
-
onto its attributes.
|
|
16
|
-
|
|
17
|
-
The goal was to provide a convenient way of loading and saving
|
|
18
|
-
configuration in a familiar human readable format. This is a bit more
|
|
19
|
-
flexible than the
|
|
20
|
-
`configparser <https://docs.python.org/3/library/configparser.html>`__
|
|
21
|
-
module which is included with Python.
|
|
22
|
-
|
|
23
|
-
This works is tested and working on Python 2.7+ and Python 3.3+. It will
|
|
24
|
-
not work on 2.6 or lower, but is expected to work on 3.0-3.2. The tests
|
|
25
|
-
do not work in 3.2.6 due to
|
|
26
|
-
`mistreating <https://travis-ci.org/Code0x58/python-jsonstore/jobs/198150401>`__
|
|
27
|
-
the 💩 when parsing the test code. This is also tested on pypy and pypy3.
|
|
28
|
-
|
|
29
|
-
Examples
|
|
30
|
-
--------
|
|
31
|
-
|
|
32
|
-
Basics
|
|
33
|
-
~~~~~~
|
|
34
|
-
|
|
35
|
-
.. code:: python
|
|
36
|
-
|
|
37
|
-
# by default JsonStore commits on every change unless in a transaction
|
|
38
|
-
store = JsonStore('config.json')
|
|
39
|
-
store.a_string = "something"
|
|
40
|
-
store.a_list = [1, 2, 3]
|
|
41
|
-
store.a_dictionary = {
|
|
42
|
-
'dict-list': [{}],
|
|
43
|
-
'ln(2)': 0.69314718056,
|
|
44
|
-
'for-you': u"💐",
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
# you can use […] to set/get/delete string keys
|
|
48
|
-
store['some_key'] = "a value"
|
|
49
|
-
# the key is split on '.'s and works on dictionaries
|
|
50
|
-
del store['a_dictionary.dict-list']
|
|
51
|
-
store['a_dictionary.new_value'] = "old value"
|
|
52
|
-
# you can also use the syntactic sugar for tuple keys (explicit lists work too)
|
|
53
|
-
assert store['a_dictionary', 'new_value'] == "old value"
|
|
54
|
-
# you can traverse lists too
|
|
55
|
-
assert store['a_list', -1] == 3
|
|
56
|
-
# you can use slices in lists
|
|
57
|
-
assert len(store['a_list', 1:]) == 2
|
|
58
|
-
|
|
59
|
-
# deep copies are made when assigning values
|
|
60
|
-
my_list = ['fun']
|
|
61
|
-
store.a_list = my_list
|
|
62
|
-
assert store.a_list is not my_list
|
|
63
|
-
assert 'a_list' in store
|
|
64
|
-
|
|
65
|
-
# deep copies are also returned to avoid unsanitary changes being made
|
|
66
|
-
store.a_dictionary['new_value'] = "new value" # won't update the store!
|
|
67
|
-
assert store.a_dictionary['new_value'] == "old value"
|
|
68
|
-
assert store.a_dictionary is not store.a_dictionary
|
|
69
|
-
|
|
70
|
-
Transactions
|
|
71
|
-
~~~~~~~~~~~~
|
|
72
|
-
|
|
73
|
-
``JsonStore`` objects can be used as `context
|
|
74
|
-
managers <https://www.python.org/dev/peps/pep-0343/>`__ to provide
|
|
75
|
-
transactions which are rolled back in the event of an exception. The
|
|
76
|
-
transaction model is primitive; you can only nest transactions.
|
|
77
|
-
|
|
78
|
-
While a store is put into a transaction, it will not save changes to
|
|
79
|
-
file until all of the transactions have been closed.
|
|
80
|
-
|
|
81
|
-
.. code:: python
|
|
82
|
-
|
|
83
|
-
from jsonstore import JsonStore
|
|
84
|
-
|
|
85
|
-
# even with auto_commit=True, the file won't be saved until the last contexts has been closed
|
|
86
|
-
with JsonStore('config.json', indent=None, auto_commit=False) as store:
|
|
87
|
-
self.value = 1
|
|
88
|
-
|
|
89
|
-
# the context manager will roll back changes made if an exception is raised
|
|
90
|
-
store = JsonStore('config.json', indent=None)
|
|
91
|
-
try:
|
|
92
|
-
with store:
|
|
93
|
-
store.value = "new"
|
|
94
|
-
raise Exception
|
|
95
|
-
except Exception:
|
|
96
|
-
pass
|
|
97
|
-
# here we see the value that was saved previously
|
|
98
|
-
assert store.value == 1
|
|
99
|
-
|
|
100
|
-
.. |Build Status| image:: https://travis-ci.org/Code0x58/python-jsonstore.svg?branch=master
|
|
101
|
-
:target: https://travis-ci.org/Code0x58/python-jsonstore
|
|
102
|
-
.. |Codacy Rating| image:: https://api.codacy.com/project/badge/Grade/37ea488773444de59469a3775be83faf
|
|
103
|
-
:target: https://www.codacy.com/app/evilumbrella-github/python-jsonstore?utm_source=github.com&utm_medium=referral&utm_content=Code0x58/python-jsonstore&utm_campaign=Badge_Grade
|
|
104
|
-
.. |PyPi Package| image:: https://badge.fury.io/py/python-jsonstore.svg
|
|
105
|
-
:target: https://pypi.org/project/python-jsonstore/
|
|
106
|
-
|
|
107
|
-
Keywords: json key value store
|
|
108
|
-
Platform: UNKNOWN
|
|
109
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
110
|
-
Classifier: Intended Audience :: Developers
|
|
111
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
112
|
-
Classifier: Operating System :: OS Independent
|
|
113
|
-
Classifier: Programming Language :: Python :: 2
|
|
114
|
-
Classifier: Programming Language :: Python :: 2.7
|
|
115
|
-
Classifier: Programming Language :: Python :: 3
|
|
116
|
-
Classifier: Programming Language :: Python :: 3.3
|
|
117
|
-
Classifier: Programming Language :: Python :: 3.4
|
|
118
|
-
Classifier: Programming Language :: Python :: 3.5
|
|
119
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
120
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
121
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
122
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
123
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
124
|
-
Classifier: Topic :: Database
|
|
125
|
-
Classifier: Topic :: Software Development
|
|
126
|
-
Description-Content-Type: text/x-rst
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: python-jsonstore
|
|
3
|
-
Version: 1.2.0
|
|
4
|
-
Summary: UNKNOWN
|
|
5
|
-
Home-page: https://github.com/Code0x58/python-jsonstore/
|
|
6
|
-
Author: Oliver Bristow
|
|
7
|
-
Author-email: github+pypi@oliverbristow.co.uk
|
|
8
|
-
License: MIT
|
|
9
|
-
Description: |PyPi Package| |Build Status| |Codacy Rating|
|
|
10
|
-
|
|
11
|
-
jsonstore
|
|
12
|
-
=========
|
|
13
|
-
|
|
14
|
-
This module provides a class that maps keys and values from a JSON file
|
|
15
|
-
onto its attributes.
|
|
16
|
-
|
|
17
|
-
The goal was to provide a convenient way of loading and saving
|
|
18
|
-
configuration in a familiar human readable format. This is a bit more
|
|
19
|
-
flexible than the
|
|
20
|
-
`configparser <https://docs.python.org/3/library/configparser.html>`__
|
|
21
|
-
module which is included with Python.
|
|
22
|
-
|
|
23
|
-
This works is tested and working on Python 2.7+ and Python 3.3+. It will
|
|
24
|
-
not work on 2.6 or lower, but is expected to work on 3.0-3.2. The tests
|
|
25
|
-
do not work in 3.2.6 due to
|
|
26
|
-
`mistreating <https://travis-ci.org/Code0x58/python-jsonstore/jobs/198150401>`__
|
|
27
|
-
the 💩 when parsing the test code. This is also tested on pypy and pypy3.
|
|
28
|
-
|
|
29
|
-
Examples
|
|
30
|
-
--------
|
|
31
|
-
|
|
32
|
-
Basics
|
|
33
|
-
~~~~~~
|
|
34
|
-
|
|
35
|
-
.. code:: python
|
|
36
|
-
|
|
37
|
-
# by default JsonStore commits on every change unless in a transaction
|
|
38
|
-
store = JsonStore('config.json')
|
|
39
|
-
store.a_string = "something"
|
|
40
|
-
store.a_list = [1, 2, 3]
|
|
41
|
-
store.a_dictionary = {
|
|
42
|
-
'dict-list': [{}],
|
|
43
|
-
'ln(2)': 0.69314718056,
|
|
44
|
-
'for-you': u"💐",
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
# you can use […] to set/get/delete string keys
|
|
48
|
-
store['some_key'] = "a value"
|
|
49
|
-
# the key is split on '.'s and works on dictionaries
|
|
50
|
-
del store['a_dictionary.dict-list']
|
|
51
|
-
store['a_dictionary.new_value'] = "old value"
|
|
52
|
-
# you can also use the syntactic sugar for tuple keys (explicit lists work too)
|
|
53
|
-
assert store['a_dictionary', 'new_value'] == "old value"
|
|
54
|
-
# you can traverse lists too
|
|
55
|
-
assert store['a_list', -1] == 3
|
|
56
|
-
# you can use slices in lists
|
|
57
|
-
assert len(store['a_list', 1:]) == 2
|
|
58
|
-
|
|
59
|
-
# deep copies are made when assigning values
|
|
60
|
-
my_list = ['fun']
|
|
61
|
-
store.a_list = my_list
|
|
62
|
-
assert store.a_list is not my_list
|
|
63
|
-
assert 'a_list' in store
|
|
64
|
-
|
|
65
|
-
# deep copies are also returned to avoid unsanitary changes being made
|
|
66
|
-
store.a_dictionary['new_value'] = "new value" # won't update the store!
|
|
67
|
-
assert store.a_dictionary['new_value'] == "old value"
|
|
68
|
-
assert store.a_dictionary is not store.a_dictionary
|
|
69
|
-
|
|
70
|
-
Transactions
|
|
71
|
-
~~~~~~~~~~~~
|
|
72
|
-
|
|
73
|
-
``JsonStore`` objects can be used as `context
|
|
74
|
-
managers <https://www.python.org/dev/peps/pep-0343/>`__ to provide
|
|
75
|
-
transactions which are rolled back in the event of an exception. The
|
|
76
|
-
transaction model is primitive; you can only nest transactions.
|
|
77
|
-
|
|
78
|
-
While a store is put into a transaction, it will not save changes to
|
|
79
|
-
file until all of the transactions have been closed.
|
|
80
|
-
|
|
81
|
-
.. code:: python
|
|
82
|
-
|
|
83
|
-
from jsonstore import JsonStore
|
|
84
|
-
|
|
85
|
-
# even with auto_commit=True, the file won't be saved until the last contexts has been closed
|
|
86
|
-
with JsonStore('config.json', indent=None, auto_commit=False) as store:
|
|
87
|
-
self.value = 1
|
|
88
|
-
|
|
89
|
-
# the context manager will roll back changes made if an exception is raised
|
|
90
|
-
store = JsonStore('config.json', indent=None)
|
|
91
|
-
try:
|
|
92
|
-
with store:
|
|
93
|
-
store.value = "new"
|
|
94
|
-
raise Exception
|
|
95
|
-
except Exception:
|
|
96
|
-
pass
|
|
97
|
-
# here we see the value that was saved previously
|
|
98
|
-
assert store.value == 1
|
|
99
|
-
|
|
100
|
-
.. |Build Status| image:: https://travis-ci.org/Code0x58/python-jsonstore.svg?branch=master
|
|
101
|
-
:target: https://travis-ci.org/Code0x58/python-jsonstore
|
|
102
|
-
.. |Codacy Rating| image:: https://api.codacy.com/project/badge/Grade/37ea488773444de59469a3775be83faf
|
|
103
|
-
:target: https://www.codacy.com/app/evilumbrella-github/python-jsonstore?utm_source=github.com&utm_medium=referral&utm_content=Code0x58/python-jsonstore&utm_campaign=Badge_Grade
|
|
104
|
-
.. |PyPi Package| image:: https://badge.fury.io/py/python-jsonstore.svg
|
|
105
|
-
:target: https://pypi.org/project/python-jsonstore/
|
|
106
|
-
|
|
107
|
-
Keywords: json key value store
|
|
108
|
-
Platform: UNKNOWN
|
|
109
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
110
|
-
Classifier: Intended Audience :: Developers
|
|
111
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
112
|
-
Classifier: Operating System :: OS Independent
|
|
113
|
-
Classifier: Programming Language :: Python :: 2
|
|
114
|
-
Classifier: Programming Language :: Python :: 2.7
|
|
115
|
-
Classifier: Programming Language :: Python :: 3
|
|
116
|
-
Classifier: Programming Language :: Python :: 3.3
|
|
117
|
-
Classifier: Programming Language :: Python :: 3.4
|
|
118
|
-
Classifier: Programming Language :: Python :: 3.5
|
|
119
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
120
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
121
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
122
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
123
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
124
|
-
Classifier: Topic :: Database
|
|
125
|
-
Classifier: Topic :: Software Development
|
|
126
|
-
Description-Content-Type: text/x-rst
|
python-jsonstore-1.2.0/setup.py
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import codecs
|
|
2
|
-
from os import path
|
|
3
|
-
from textwrap import dedent
|
|
4
|
-
|
|
5
|
-
from setuptools import setup
|
|
6
|
-
|
|
7
|
-
here = path.abspath(path.dirname(__file__))
|
|
8
|
-
|
|
9
|
-
with codecs.open(path.join(here, "README.rst"), encoding='utf-8') as f:
|
|
10
|
-
long_description = f.read()
|
|
11
|
-
|
|
12
|
-
setup(
|
|
13
|
-
name='python-jsonstore',
|
|
14
|
-
use_scm_version=True,
|
|
15
|
-
description="",
|
|
16
|
-
long_description=long_description,
|
|
17
|
-
long_description_content_type='text/x-rst',
|
|
18
|
-
author="Oliver Bristow",
|
|
19
|
-
author_email='github+pypi@oliverbristow.co.uk',
|
|
20
|
-
license='MIT',
|
|
21
|
-
classifiers=dedent("""
|
|
22
|
-
Development Status :: 5 - Production/Stable
|
|
23
|
-
Intended Audience :: Developers
|
|
24
|
-
License :: OSI Approved :: MIT License
|
|
25
|
-
Operating System :: OS Independent
|
|
26
|
-
Programming Language :: Python :: 2
|
|
27
|
-
Programming Language :: Python :: 2.7
|
|
28
|
-
Programming Language :: Python :: 3
|
|
29
|
-
Programming Language :: Python :: 3.3
|
|
30
|
-
Programming Language :: Python :: 3.4
|
|
31
|
-
Programming Language :: Python :: 3.5
|
|
32
|
-
Programming Language :: Python :: 3.6
|
|
33
|
-
Programming Language :: Python :: 3.7
|
|
34
|
-
Programming Language :: Python :: 3.8
|
|
35
|
-
Programming Language :: Python :: Implementation :: CPython
|
|
36
|
-
Programming Language :: Python :: Implementation :: PyPy
|
|
37
|
-
Topic :: Database
|
|
38
|
-
Topic :: Software Development
|
|
39
|
-
""").strip().split('\n'),
|
|
40
|
-
keywords='json key value store',
|
|
41
|
-
url='https://github.com/Code0x58/python-jsonstore/',
|
|
42
|
-
py_modules=dedent("""
|
|
43
|
-
jsonstore
|
|
44
|
-
""").strip().split('\n'),
|
|
45
|
-
setup_requires=["setuptools_scm", "wheel"],
|
|
46
|
-
)
|
|
File without changes
|
{python-jsonstore-1.2.0 → python_jsonstore-1.3.3}/python_jsonstore.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|