Skip to content

Commit dc98a19

Browse files
author
Romain Bosa
committed
Replace base_state counter with a custom picklable one
1 parent 1913754 commit dc98a19

File tree

1 file changed

+102
-25
lines changed

1 file changed

+102
-25
lines changed

jsf/parser.py

Lines changed: 102 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from collections import ChainMap
55
from copy import deepcopy
66
from datetime import datetime
7-
from itertools import count
87
from pathlib import Path
98
from types import MappingProxyType
109
from typing import Any, Dict, List, Optional, Tuple, Union
@@ -68,7 +67,7 @@ def __init__(
6867
self.root_schema = schema
6968
self.definitions = {}
7069
self.base_state = {
71-
"__counter__": count(start=1),
70+
"__counter__": _PicklableCounter(),
7271
"__all_json_paths__": [],
7372
"__depth__": 0,
7473
**initial_state,
@@ -107,10 +106,16 @@ def from_json(
107106
"""
108107
with open(path) as f:
109108
return JSF(
110-
json.load(f), context, initial_state, allow_none_optionals, max_recursive_depth
109+
json.load(f),
110+
context,
111+
initial_state,
112+
allow_none_optionals,
113+
max_recursive_depth,
111114
)
112115

113-
def __parse_primitive(self, name: str, path: str, schema: Dict[str, Any]) -> PrimitiveTypes:
116+
def __parse_primitive(
117+
self, name: str, path: str, schema: Dict[str, Any]
118+
) -> PrimitiveTypes:
114119
item_type, is_nullable = self.__is_field_nullable(schema)
115120
cls = Primitives.get(item_type)
116121
return cls.from_dict(
@@ -125,7 +130,11 @@ def __parse_primitive(self, name: str, path: str, schema: Dict[str, Any]) -> Pri
125130
)
126131

127132
def __parse_object(
128-
self, name: str, path: str, schema: Dict[str, Any], root: Optional[AllTypes] = None
133+
self,
134+
name: str,
135+
path: str,
136+
schema: Dict[str, Any],
137+
root: Optional[AllTypes] = None,
129138
) -> Object:
130139
_, is_nullable = self.__is_field_nullable(schema)
131140
model = Object.from_dict(
@@ -142,20 +151,28 @@ def __parse_object(
142151
props = []
143152
for _name, definition in schema.get("properties", {}).items():
144153
props.append(
145-
self.__parse_definition(_name, path=f"{path}/{_name}", schema=definition, root=root)
154+
self.__parse_definition(
155+
_name, path=f"{path}/{_name}", schema=definition, root=root
156+
)
146157
)
147158
model.properties = props
148159
pattern_props = []
149160
for _name, definition in schema.get("patternProperties", {}).items():
150161
pattern_props.append(
151-
self.__parse_definition(_name, path=f"{path}/{_name}", schema=definition, root=root)
162+
self.__parse_definition(
163+
_name, path=f"{path}/{_name}", schema=definition, root=root
164+
)
152165
)
153166
model.patternProperties = pattern_props
154167

155168
return model
156169

157170
def __parse_array(
158-
self, name: str, path: str, schema: Dict[str, Any], root: Optional[AllTypes] = None
171+
self,
172+
name: str,
173+
path: str,
174+
schema: Dict[str, Any],
175+
root: Optional[AllTypes] = None,
159176
) -> Array:
160177
_, is_nullable = self.__is_field_nullable(schema)
161178
arr = Array.from_dict(
@@ -169,11 +186,17 @@ def __parse_array(
169186
}
170187
)
171188
root = arr if root is None else root
172-
arr.items = self.__parse_definition(name, f"{path}/items", schema["items"], root=root)
189+
arr.items = self.__parse_definition(
190+
name, f"{path}/items", schema["items"], root=root
191+
)
173192
return arr
174193

175194
def __parse_tuple(
176-
self, name: str, path: str, schema: Dict[str, Any], root: Optional[AllTypes] = None
195+
self,
196+
name: str,
197+
path: str,
198+
schema: Dict[str, Any],
199+
root: Optional[AllTypes] = None,
177200
) -> JSFTuple:
178201
_, is_nullable = self.__is_field_nullable(schema)
179202
arr = JSFTuple.from_dict(
@@ -190,7 +213,9 @@ def __parse_tuple(
190213
arr.items = []
191214
for i, item in enumerate(schema["items"]):
192215
arr.items.append(
193-
self.__parse_definition(name, path=f"{path}/{name}[{i}]", schema=item, root=root)
216+
self.__parse_definition(
217+
name, path=f"{path}/{name}[{i}]", schema=item, root=root
218+
)
194219
)
195220
return arr
196221

@@ -207,9 +232,15 @@ def __is_field_nullable(self, schema: Dict[str, Any]) -> Tuple[str, bool]:
207232
return item_type, False
208233

209234
def __parse_anyOf(
210-
self, name: str, path: str, schema: Dict[str, Any], root: Optional[AllTypes] = None
235+
self,
236+
name: str,
237+
path: str,
238+
schema: Dict[str, Any],
239+
root: Optional[AllTypes] = None,
211240
) -> AnyOf:
212-
model = AnyOf(name=name, path=path, max_recursive_depth=self.max_recursive_depth, **schema)
241+
model = AnyOf(
242+
name=name, path=path, max_recursive_depth=self.max_recursive_depth, **schema
243+
)
213244
root = model if root is None else root
214245
schemas = []
215246
for d in schema["anyOf"]:
@@ -218,18 +249,32 @@ def __parse_anyOf(
218249
return model
219250

220251
def __parse_allOf(
221-
self, name: str, path: str, schema: Dict[str, Any], root: Optional[AllTypes] = None
252+
self,
253+
name: str,
254+
path: str,
255+
schema: Dict[str, Any],
256+
root: Optional[AllTypes] = None,
222257
) -> AllOf:
223258
combined_schema = dict(ChainMap(*schema["allOf"]))
224-
model = AllOf(name=name, path=path, max_recursive_depth=self.max_recursive_depth, **schema)
259+
model = AllOf(
260+
name=name, path=path, max_recursive_depth=self.max_recursive_depth, **schema
261+
)
225262
root = model if root is None else root
226-
model.combined_schema = self.__parse_definition(name, path, combined_schema, root=root)
263+
model.combined_schema = self.__parse_definition(
264+
name, path, combined_schema, root=root
265+
)
227266
return model
228267

229268
def __parse_oneOf(
230-
self, name: str, path: str, schema: Dict[str, Any], root: Optional[AllTypes] = None
269+
self,
270+
name: str,
271+
path: str,
272+
schema: Dict[str, Any],
273+
root: Optional[AllTypes] = None,
231274
) -> OneOf:
232-
model = OneOf(name=name, path=path, max_recursive_depth=self.max_recursive_depth, **schema)
275+
model = OneOf(
276+
name=name, path=path, max_recursive_depth=self.max_recursive_depth, **schema
277+
)
233278
root = model if root is None else root
234279
schemas = []
235280
for d in schema["oneOf"]:
@@ -247,13 +292,20 @@ def __parse_named_definition(self, path: str, def_name: str, root) -> AllTypes:
247292
definition = schema.get(def_tag, {}).get(def_name)
248293
if definition is not None:
249294
parsed_definition = self.__parse_definition(
250-
def_name, path=f"{path}/#/{def_tag}/{def_name}", schema=definition, root=root
295+
def_name,
296+
path=f"{path}/#/{def_tag}/{def_name}",
297+
schema=definition,
298+
root=root,
251299
)
252300
self.definitions[f"#/{def_tag}/{def_name}"] = parsed_definition
253301
return parsed_definition
254302

255303
def __parse_definition(
256-
self, name: str, path: str, schema: Dict[str, Any], root: Optional[AllTypes] = None
304+
self,
305+
name: str,
306+
path: str,
307+
schema: Dict[str, Any],
308+
root: Optional[AllTypes] = None,
257309
) -> AllTypes:
258310
self.base_state["__all_json_paths__"].append(path)
259311
item_type, is_nullable = self.__is_field_nullable(schema)
@@ -264,7 +316,8 @@ def __parse_definition(
264316
enum_list = schema["enum"]
265317
assert len(enum_list) > 0, "Enum List is Empty"
266318
assert all(
267-
isinstance(item, (int, float, str, dict, type(None))) for item in enum_list
319+
isinstance(item, (int, float, str, dict, type(None)))
320+
for item in enum_list
268321
), "Enum Type is not null, int, float, string or dict"
269322
return JSFEnum.from_dict(
270323
{
@@ -286,7 +339,9 @@ def __parse_definition(
286339
elif item_type == "object" and "oneOf" in schema:
287340
return self.__parse_oneOf(name, path, schema, root)
288341
elif item_type == "array":
289-
if (schema.get("contains") is not None) or isinstance(schema.get("items"), dict):
342+
if (schema.get("contains") is not None) or isinstance(
343+
schema.get("items"), dict
344+
):
290345
return self.__parse_array(name, path, schema, root)
291346
if isinstance(schema.get("items"), list) and all(
292347
isinstance(x, dict) for x in schema.get("items", [])
@@ -320,7 +375,9 @@ def __parse_definition(
320375
elif "oneOf" in schema:
321376
return self.__parse_oneOf(name, path, schema, root)
322377
elif not any(key in schema for key in ["not", "if", "then", "else"]):
323-
return self.__parse_primitive(name, path, {**schema, "type": list(Primitives.keys())})
378+
return self.__parse_primitive(
379+
name, path, {**schema, "type": list(Primitives.keys())}
380+
)
324381
else:
325382
raise ValueError(f"Cannot parse schema {repr(schema)}") # pragma: no cover
326383

@@ -340,7 +397,11 @@ def context(self):
340397
return {**self.base_context, "state": deepcopy(self.base_state)}
341398

342399
def generate(
343-
self, n: Optional[int] = None, *, use_defaults: bool = False, use_examples: bool = False
400+
self,
401+
n: Optional[int] = None,
402+
*,
403+
use_defaults: bool = False,
404+
use_examples: bool = False,
344405
) -> Any:
345406
"""Generates a fake object from the provided schema, and returns the
346407
output.
@@ -350,7 +411,11 @@ def generate(
350411
use_defaults (bool, optional): prefer the default value as defined in the schema over a randomly generated object. Defaults to False.
351412
use_examples (bool, optional): prefer an example as defined in the schema over a randomly generated object. This parameter is preceded by the `use_defaults` parameter if set. Defaults to False.
352413
"""
353-
context = {**self.context, "use_defaults": use_defaults, "use_examples": use_examples}
414+
context = {
415+
**self.context,
416+
"use_defaults": use_defaults,
417+
"use_examples": use_examples,
418+
}
354419
if n is None or n == 1:
355420
return self.root.generate(context=context)
356421
return [self.root.generate(context=context) for _ in range(n)]
@@ -371,3 +436,15 @@ def to_json(self, path: Path, **kwargs) -> None:
371436
output to the given path."""
372437
with open(path, "w") as f:
373438
json.dump(self.generate(), f, **kwargs)
439+
440+
441+
class _PicklableCounter:
442+
def __init__(self):
443+
self._counter = 0
444+
445+
def __iter__(self):
446+
return self
447+
448+
def __next__(self):
449+
self._counter += 1
450+
return self._counter

0 commit comments

Comments
 (0)