Package yapydata ::
Package datatree ::
Module synjson
1
2 """The *YapyData.json* module provides *JSON* access in compliance to RFC-7159 [RFC7159]_.
3 """
4
5 import os
6
7 import json as myjson
8
9 from pythonids import ISSTR
10
11 from yapydata.datatree import YapyDataTreeError
12 from yapydata.datatree.datatree import DataTree, YapyDataDataTreeOidError
13
14 __author__ = 'Arno-Can Uestuensoez'
15 __license__ = "Artistic-License-2.0 + Forced-Fairplay-Constraints"
16 __copyright__ = "Copyright (C) 2019 Arno-Can Uestuensoez" \
17 " @Ingenieurbuero Arno-Can Uestuensoez"
18 __version__ = '0.1.1'
19 __uuid__ = "60cac28d-efe6-4a8d-802f-fa4fc94fa741"
20
21 __docformat__ = "restructuredtext en"
25 """Basic JSON syntax error.
26 """
27 pass
28
31 """For API call-compliance with other syntaxes. Returns here the
32 input tree only.
33
34 Args:
35 xval:
36 The input tree from the *DataTreeJSON* - which is
37 the result from *json.load()*.
38
39 Returns:
40 The returns here the input *xval*.
41
42 Raises:
43 pass-through
44
45 """
46 return xval
47
50 """Creates a new branch including the assigned value to
51 the last node. The node types are defined by the types
52 of the *subpath* entries.
53
54 Supports a single linear branch only, no sub-branching.
55
56 The created path is validated for permitted types.
57 The derived types such as JSON have to support their
58 own branch method. Thus provided as a static method.
59
60 Args:
61 subpath:
62 Variable list/tuple of path keys and indexes.
63
64 kargs:
65 value:
66 Value to be assigned to the final node.
67
68 Returns:
69 A created branch.
70
71 Raises:
72 pass-through
73
74 """
75 _val = kargs.get('value')
76 _subpath=list(subpath)
77 try:
78 ik = _subpath.pop(0)
79 except IndexError:
80 return _val
81
82 if isinstance(ik, int):
83 if ik != 0:
84
85 raise YapyDataDataTreeOidError(
86 "new list requires idx==0: %s\n see: %s\n" %(
87 str(subpath),
88 str(ik)
89 )
90 )
91 return [grow_branch(*_subpath, value=_val)]
92
93 elif isinstance(ik, ISSTR):
94
95 return {ik: grow_branch(*_subpath, value=_val)}
96
97 raise YapyDataDataTreeOidError(
98 "invalid subpath key/index: %s\n see: %s\n" %(
99 str(subpath),
100 str(ik)
101 )
102 )
103
106 """Provides JSON RFC-7159 compliant in-memory data trees.
107 """
108
109 @staticmethod
111 """Validate conformance of top-node to RFC-7159.
112 """
113
114 if (
115 not isinstance(
116 value,
117 (dict, list, int, float,)
118 )
119 and value not in (None, True, False,)
120 and not isinstance(value, ISSTR)
121 ):
122 raise YapyDataJSONError(
123 "top 'node' must be a valid JSON-RFC-7159 type, got: "
124 + str(type(value))
125 )
126
127 - def __init__(self, data=None, **kargs):
128 """
129 Args:
130 data:
131 A JSON compliant in-memory data tree in accordance to RFC-7159::
132
133 json-value := (
134 object | array
135 | number
136 | string
137 | false | true
138 | null
139 )
140
141 The equivalent *Python* types are::
142
143 data := <RFC-7159-type-for-json>
144
145 RFC-7159-type-for-json := (
146 dict | list # see: object, array
147 | int | float # see: number
148 | str # see: unicode / for Python: ISSTR = (str(3) | unicode(2))
149 | None | True | False # see: null, true, false
150 )
151
152 The initial data defines the permitted type of the first item
153 within the *subpath* of the spanned data tree.
154
155 Thus atomic data types define a single node data tree only - new in RFC-7159.
156
157 Returns:
158 None / initialized object
159
160 Raises:
161 YapyDataDataTreeError
162
163 pass-through
164
165 """
166 DataTreeJSON.isvalid_top(data)
167 super(DataTreeJSON, self).__init__(data)
168
170 """Validates types of own data attributes.
171
172 Args:
173 name:
174 Name of the attribute. Following are reserved and
175 treated special:
176
177 * type: str - 'data'
178 The value is treated as the replacement of the internal
179 data attribute. Replaces or creates the complete data
180 of teh current instance.
181
182 value:
183 The value of the attribute. This by default superposes
184 present values by replacement. Non-present are created.
185
186 Returns:
187
188 Raises:
189 YapyDataDataTreeError
190
191 """
192 if name == 'data':
193
194
195
196 DataTreeJSON.isvalid_top(value)
197 self.__dict__[name] = value
198
199 else:
200
201
202
203 return object.__setattr__(self, name, value)
204
205 - def create(self, *subpath, **kargs):
214
215 - def import_data(self, input, key=None, node=None, **kargs):
216 """Reads a JSON file. This is a simple basic method for the application
217 on the lower layers of the software stack. It is designed for minimal
218 dependencies. The used library is the standard *json* package.
219 The data is not validated.
220
221 Args:
222 input:
223 The source of the *JSON* string data::
224
225 input := (
226 <fpname> # file path name
227 | <file-id> # file pointer id
228 | <io-stream> # io stream id
229 )
230
231 fpname := <json-file-path-name>
232 json-file-path-name := (
233 <file-path-name> # with extension
234 | <file-path-name> '.json' # without extension, for multiple syntaxes
235 )
236 file-id := "open file: file_id = open(<fpname>)"
237 io-stream := "open io stream - io_stream = io.StreamIO(<json-string>)"
238 json-string := "a valid string in accordance to RFC-7159"
239
240 key:
241 The key for the insertion point::
242
243 node[key] = <file-data>
244
245 default := None - replace self.data,
246
247 The caller is responsible for the containment of the provided
248 node within the data structure represented by this object. No
249 checks are performed.
250
251 node:
252 The node for the insertion of the read data.::
253
254 default := <top>
255
256 Returns:
257 Reference to read data structure.
258
259 Raises:
260 YapyDataConfigError
261
262 pass-through
263
264 """
265 if isinstance(input, ISSTR):
266 if os.path.isfile(input):
267
268 datafile = os.path.abspath(input)
269 elif os.path.isfile(input + '.json'):
270
271 datafile = os.path.abspath(input) + '.json'
272 else:
273 raise YapyDataTreeError("Missing file: " + str(input))
274
275 with open(datafile) as data_file:
276 jval = myjson.load(data_file)
277
278 elif hasattr(input, 'read'):
279 jval = myjson.load(input)
280
281 else:
282 raise YapyDataTreeError("Cannot read data: " + str(input))
283
284 if key and node == None:
285 raise YapyDataTreeError("Given key(%s) requires a valid node." % (str(key)))
286
287 if key:
288 node[key] = jval
289 else:
290 self.data = jval
291
292 return jval
293