diff --git a/examples/fastapi/api.py b/examples/fastapi/api.py index f784b26..6c2ebbb 100644 --- a/examples/fastapi/api.py +++ b/examples/fastapi/api.py @@ -1,6 +1,7 @@ -from jsf import JSF from fastapi import FastAPI +from jsf import JSF + app = FastAPI(docs_url="/") generator = JSF.from_json("custom.json") diff --git a/examples/fastapi/model.py b/examples/fastapi/model.py index 1bd448f..7f45b45 100644 --- a/examples/fastapi/model.py +++ b/examples/fastapi/model.py @@ -11,62 +11,34 @@ class NewbornItem(BaseModel): surname: str = Field(..., description="The newborn's surname, eg: Reid") - givenNames: str = Field( - ..., description="The newborn's given names, eg: Mathew David" - ) + givenNames: str = Field(..., description="The newborn's given names, eg: Mathew David") sex: str = Field(..., description="The newborn's sex, eg: M, F or U") - dateOfBirth: str = Field( - ..., description="The newborn's date of birth, eg: 17/03/2021" - ) + dateOfBirth: str = Field(..., description="The newborn's date of birth, eg: 17/03/2021") birthOrder: str = Field(..., description="The newborn's birth order, eg: 1") - indigenousStatus: str = Field( - ..., description="The newborn's indigenous status, eg: 14" - ) - uniqueId: str = Field( - ..., description="The newborn's unique birth event id, eg: 20474417" - ) + indigenousStatus: str = Field(..., description="The newborn's indigenous status, eg: 14") + uniqueId: str = Field(..., description="The newborn's unique birth event id, eg: 20474417") class Address(BaseModel): - suburb: str = Field( - ..., description='The address suburb (Australia Only), eg: Watson' - ) - postcode: str = Field( - ..., description='The address postcode (Australia Only), eg: 2602' - ) + suburb: str = Field(..., description="The address suburb (Australia Only), eg: Watson") + postcode: str = Field(..., description="The address postcode (Australia Only), eg: 2602") street1: str = Field( ..., - description='The address street name line 1 (Australia Only), eg: 49 Aspinall St', - ) - street2: str = Field( - ..., description='The address street name line 2 (Australia Only), eg: Suite 1' + description="The address street name line 1 (Australia Only), eg: 49 Aspinall St", ) + street2: str = Field(..., description="The address street name line 2 (Australia Only), eg: Suite 1") class Parent(BaseModel): - surname: Optional[str] = Field( - ..., description="The mother's surname, eg: Mcdermott" - ) - givenNames: str = Field( - ..., description="The mother's given names, eg: Sarah Lousie" - ) + surname: Optional[str] = Field(..., description="The mother's surname, eg: Mcdermott") + givenNames: str = Field(..., description="The mother's given names, eg: Sarah Lousie") mailAddress: Address residentialAddress: Address - mobile: str = Field( - ..., description="The mother's mobile phone number, eg: 0400182545" - ) - homePhone: str = Field( - ..., description="The mother's home phone number, eg: 0245458450" - ) - email: str = Field( - ..., description="The mother's email address, eg: jesse6565656565@gmail.com" - ) - hospital: str = Field( - ..., description='The hospital where the birth took place, eg: ACTCC' - ) - dateReceived: str = Field( - ..., description='The date the birth event was received, eg: 17/03/2021' - ) + mobile: str = Field(..., description="The mother's mobile phone number, eg: 0400182545") + homePhone: str = Field(..., description="The mother's home phone number, eg: 0245458450") + email: str = Field(..., description="The mother's email address, eg: jesse6565656565@gmail.com") + hospital: str = Field(..., description="The hospital where the birth took place, eg: ACTCC") + dateReceived: str = Field(..., description="The date the birth event was received, eg: 17/03/2021") personId: str = Field(..., description="The mother's personId, eg: 123456789") diff --git a/jsf/parser.py b/jsf/parser.py index bcbdbd8..8a0cf8d 100644 --- a/jsf/parser.py +++ b/jsf/parser.py @@ -20,8 +20,8 @@ JSFTuple, Object, OneOf, - PrimitiveTypes, Primitives, + PrimitiveTypes, ) logger = logging.getLogger() @@ -66,9 +66,7 @@ def __parse_object(self, name: str, path: str, schema: Dict[str, Any]) -> Object model.properties = props pattern_props = [] for _name, definition in schema.get("patternProperties", {}).items(): - pattern_props.append( - self.__parse_definition(_name, path=f"{path}/{_name}", schema=definition) - ) + pattern_props.append(self.__parse_definition(_name, path=f"{path}/{_name}", schema=definition)) model.patternProperties = pattern_props return model @@ -142,9 +140,7 @@ def __parse_definition(self, name: str, path: str, schema: Dict[str, Any]) -> Al assert all( isinstance(item, (int, float, str, type(None))) for item in enum_list ), "Enum Type is not null, int, float or string" - return JSFEnum.from_dict( - {"name": name, "path": path, "is_nullable": is_nullable, **schema} - ) + return JSFEnum.from_dict({"name": name, "path": path, "is_nullable": is_nullable, **schema}) elif "type" in schema: if item_type == "object" and "properties" in schema: return self.__parse_object(name, path, schema) @@ -157,9 +153,7 @@ def __parse_definition(self, name: str, path: str, schema: Dict[str, Any]) -> Al elif item_type == "array": if (schema.get("contains") is not None) or isinstance(schema.get("items"), dict): return self.__parse_array(name, path, schema) - if isinstance(schema.get("items"), list) and all( - isinstance(x, dict) for x in schema.get("items", []) - ): + if isinstance(schema.get("items"), list) and all(isinstance(x, dict) for x in schema.get("items", [])): return self.__parse_tuple(name, path, schema) else: return self.__parse_primitive(name, path, schema) diff --git a/jsf/schema_types/_tuple.py b/jsf/schema_types/_tuple.py index 063152e..7bdc268 100644 --- a/jsf/schema_types/_tuple.py +++ b/jsf/schema_types/_tuple.py @@ -7,9 +7,7 @@ class JSFTuple(BaseSchema): items: Optional[List[BaseSchema]] = None - additionalItems: Optional[ - Union[bool, BaseSchema] - ] = None # TODO: Random additional items to be appended + additionalItems: Optional[Union[bool, BaseSchema]] = None # TODO: Random additional items to be appended minItems: Optional[int] = 0 maxItems: Optional[int] = 5 uniqueItems: Optional[bool] = False diff --git a/jsf/schema_types/array.py b/jsf/schema_types/array.py index d23023f..4516320 100644 --- a/jsf/schema_types/array.py +++ b/jsf/schema_types/array.py @@ -21,16 +21,12 @@ def generate(self, context: Dict[str, Any]) -> Optional[List[Any]]: try: return super().generate(context) except ProviderNotSetException: - if isinstance(self.fixed, str): self.minItems = self.maxItems = eval(self.fixed, context)() elif isinstance(self.fixed, int): self.minItems = self.maxItems = self.fixed - output = [ - self.items.generate(context) - for _ in range(random.randint(self.minItems, self.maxItems)) - ] + output = [self.items.generate(context) for _ in range(random.randint(self.minItems, self.maxItems))] if self.uniqueItems and self.items.type == "object": output = [dict(s) for s in {frozenset(d.items()) for d in output}] while len(output) < self.minItems: diff --git a/jsf/schema_types/base.py b/jsf/schema_types/base.py index e1ded1b..7e74328 100644 --- a/jsf/schema_types/base.py +++ b/jsf/schema_types/base.py @@ -38,7 +38,6 @@ def from_dict(d): raise NotImplementedError # pragma: no cover def generate(self, context: Dict[str, Any]) -> Any: - if self.set_state is not None: context["state"][self.path] = {k: eval(v, context)() for k, v in self.set_state.items()} diff --git a/jsf/schema_types/number.py b/jsf/schema_types/number.py index 9be5330..f2ddfb5 100644 --- a/jsf/schema_types/number.py +++ b/jsf/schema_types/number.py @@ -17,7 +17,6 @@ def generate(self, context: Dict[str, Any]) -> Optional[float]: try: return super().generate(context) except ProviderNotSetException: - step = self.multipleOf if self.multipleOf is not None else 1 if isinstance(self.exclusiveMinimum, bool): @@ -34,9 +33,7 @@ def generate(self, context: Dict[str, Any]) -> Optional[float]: else: _max = self.maximum - return float( - step * random.randint(math.ceil(float(_min) / step), math.floor(float(_max) / step)) - ) + return float(step * random.randint(math.ceil(float(_min) / step), math.floor(float(_max) / step))) def model(self, context: Dict[str, Any]): return self.to_pydantic(context, float) diff --git a/jsf/schema_types/object.py b/jsf/schema_types/object.py index f8e4e7e..9512d97 100644 --- a/jsf/schema_types/object.py +++ b/jsf/schema_types/object.py @@ -40,9 +40,7 @@ def generate(self, context: Dict[str, Any]) -> Optional[Dict[str, Any]]: try: return super().generate(context) except ProviderNotSetException: - explicit_properties = { - o.name: o.generate(context) for o in self.properties if self.should_keep(o.name) - } + explicit_properties = {o.name: o.generate(context) for o in self.properties if self.should_keep(o.name)} pattern_props = {} if self.patternProperties: for o in self.patternProperties: diff --git a/jsf/schema_types/string.py b/jsf/schema_types/string.py index 9d49f86..c4f8924 100644 --- a/jsf/schema_types/string.py +++ b/jsf/schema_types/string.py @@ -104,9 +104,7 @@ def fake_duration(): "iri": faker.uri, "iri-reference": lambda: faker.uri() + rstr.xeger(PARAM_PATTERN), "uri-template": lambda: rstr.xeger( - URI_PATTERN.format(hostname=re.escape(faker.hostname())).replace( - "(?:", "(?:/\\{[a-z][:a-zA-Z0-9-]*\\}|" - ) + URI_PATTERN.format(hostname=re.escape(faker.hostname())).replace("(?:", "(?:/\\{[a-z][:a-zA-Z0-9-]*\\}|") ), "json-pointer": lambda: rstr.xeger(f"(/(?:${FRAGMENT.replace(']*', '/]*')}|~[01]))+"), "relative-json-pointer": lambda: rstr.xeger( @@ -132,9 +130,7 @@ def generate(self, context: Dict[str, Any]) -> Optional[str]: return str(content_encoding.encode(s, self.contentEncoding)) if s else s except ProviderNotSetException: format_map["regex"] = lambda: rstr.xeger(self.pattern) - format_map["relative-json-pointer"] = lambda: random.choice( - context["state"]["__all_json_paths__"] - ) + format_map["relative-json-pointer"] = lambda: random.choice(context["state"]["__all_json_paths__"]) if format_map.get(self.format) is not None: return content_encoding.encode(format_map[self.format](), self.contentEncoding) if self.pattern is not None: diff --git a/jsf/schema_types/string_utils/content_type/application__jwt.py b/jsf/schema_types/string_utils/content_type/application__jwt.py index 0d2f60a..858a94d 100644 --- a/jsf/schema_types/string_utils/content_type/application__jwt.py +++ b/jsf/schema_types/string_utils/content_type/application__jwt.py @@ -15,7 +15,6 @@ def base64url_encode(input: bytes): def jwt(api_key, expiry, api_sec): - segments = [] header = {"typ": "JWT", "alg": "HS256"} @@ -39,7 +38,6 @@ def jwt(api_key, expiry, api_sec): def create_random_jwt(*args, **kwargs): - api_key = secrets.token_urlsafe(16) api_sec = secrets.token_urlsafe(16) diff --git a/jsf/schema_types/string_utils/content_type/image__jpeg.py b/jsf/schema_types/string_utils/content_type/image__jpeg.py index a5be57b..3c6af1d 100644 --- a/jsf/schema_types/string_utils/content_type/image__jpeg.py +++ b/jsf/schema_types/string_utils/content_type/image__jpeg.py @@ -7,7 +7,5 @@ def random_jpg(*args, **kwargs) -> str: return bytes_str_repr( - requests.get( - f"https://picsum.photos/{random.randint(1,50)*10}/{random.randint(1,50)*10}.jpg" - ).content + requests.get(f"https://picsum.photos/{random.randint(1,50)*10}/{random.randint(1,50)*10}.jpg").content ) diff --git a/jsf/schema_types/string_utils/content_type/image__webp.py b/jsf/schema_types/string_utils/content_type/image__webp.py index 83ac6f6..9871387 100644 --- a/jsf/schema_types/string_utils/content_type/image__webp.py +++ b/jsf/schema_types/string_utils/content_type/image__webp.py @@ -7,7 +7,5 @@ def random_webp(*args, **kwargs) -> str: return bytes_str_repr( - requests.get( - f"https://picsum.photos/{random.randint(1,50)*10}/{random.randint(1,50)*10}.webp" - ).content + requests.get(f"https://picsum.photos/{random.randint(1,50)*10}/{random.randint(1,50)*10}.webp").content ) diff --git a/jsf/tests/test_cli.py b/jsf/tests/test_cli.py index df425c1..e269ba4 100644 --- a/jsf/tests/test_cli.py +++ b/jsf/tests/test_cli.py @@ -1,19 +1,18 @@ import json from pathlib import Path -from jsf.cli import app from jsonschema import validate from typer.testing import CliRunner # pants: no-infer-dep +from jsf.cli import app + runner = CliRunner() def test_app(TestData): file = Path("tmp.json") try: - result = runner.invoke( - app, ["--schema", TestData / "custom.json", "--instance", "tmp.json"] - ) + result = runner.invoke(app, ["--schema", TestData / "custom.json", "--instance", "tmp.json"]) assert result.exit_code == 0 assert file.exists() with open(file, "r") as f: diff --git a/jsf/tests/test_default_fake.py b/jsf/tests/test_default_fake.py index 36fa30d..8b31a5f 100644 --- a/jsf/tests/test_default_fake.py +++ b/jsf/tests/test_default_fake.py @@ -2,6 +2,7 @@ import re import jwt # pants: no-infer-dep + from jsf.parser import JSF @@ -234,12 +235,8 @@ def test_fake_array_dicts(TestData): assert isinstance(p.generate(), dict) fake_data = [p.generate() for _ in range(1000)] assert all(len(d["Basket"]) == 2 for d in fake_data), fake_data - assert all( - d["Basket"][0]["Item Name"] in ["A", "B", "C", "D", "E"] for d in fake_data - ), fake_data - assert all( - d["Basket"][1]["Item Name"] in ["A", "B", "C", "D", "E"] for d in fake_data - ), fake_data + assert all(d["Basket"][0]["Item Name"] in ["A", "B", "C", "D", "E"] for d in fake_data), fake_data + assert all(d["Basket"][1]["Item Name"] in ["A", "B", "C", "D", "E"] for d in fake_data), fake_data assert all(0 <= d["Basket"][0]["Amount"] < 5 for d in fake_data), fake_data assert all(0 <= d["Basket"][1]["Amount"] < 5 for d in fake_data), fake_data @@ -324,8 +321,7 @@ def test_fake_string_format(TestData): assert all(bool(re.match(r".*@.*", d["email"])) for d in fake_data), fake_data assert all(bool(re.match(r".*@.*", d["idn-email"])) for d in fake_data), fake_data assert all( - bool(re.match(r"\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}\+\d{2}\:\d{2}", d["date-time"])) - for d in fake_data + bool(re.match(r"\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}\+\d{2}\:\d{2}", d["date-time"])) for d in fake_data ), fake_data assert all(bool(re.match(r"\d{4}-\d{2}-\d{2}", d["date"])) for d in fake_data), fake_data assert all( @@ -337,15 +333,9 @@ def test_fake_string_format(TestData): ) for d in fake_data ), fake_data - assert all( - bool(re.match(r"\d{2}\:\d{2}\:\d{2}\+\d{2}\:\d{2}", d["time"])) for d in fake_data - ), fake_data - assert all( - bool(re.match(r"[a-zA-Z0-9+-\.]{1,33}\.[a-z]{2,4}", d["hostname"])) for d in fake_data - ) - assert all( - bool(re.match(r"[a-zA-Z0-9+-\.]{1,33}\.[a-z]{2,4}", d["idn-hostname"])) for d in fake_data - ) + assert all(bool(re.match(r"\d{2}\:\d{2}\:\d{2}\+\d{2}\:\d{2}", d["time"])) for d in fake_data), fake_data + assert all(bool(re.match(r"[a-zA-Z0-9+-\.]{1,33}\.[a-z]{2,4}", d["hostname"])) for d in fake_data) + assert all(bool(re.match(r"[a-zA-Z0-9+-\.]{1,33}\.[a-z]{2,4}", d["idn-hostname"])) for d in fake_data) assert all(bool(re.match(r"[a-f0-9]{0,4}(:[a-f0-9]{0,4}){7}", d["ipv6"])) for d in fake_data), [ d["ipv6"] for d in fake_data ] diff --git a/jsf/tests/test_model_gen.py b/jsf/tests/test_model_gen.py index f2a5bbb..e033007 100644 --- a/jsf/tests/test_model_gen.py +++ b/jsf/tests/test_model_gen.py @@ -4,9 +4,10 @@ from typing import List, _GenericAlias import pytest # pants: no-infer-dep -from jsf.parser import JSF from pydantic.main import create_model +from jsf.parser import JSF + Object = create_model("Object") expected = [ diff --git a/jsf/tests/test_parser.py b/jsf/tests/test_parser.py index 5832b76..7ecae80 100644 --- a/jsf/tests/test_parser.py +++ b/jsf/tests/test_parser.py @@ -1,8 +1,8 @@ import json import pytest # pants: no-infer-dep -from jsf.parser import JSF +from jsf.parser import JSF from jsf.schema_types import ( Array, Boolean, @@ -95,6 +95,7 @@ def test_nested_object_ref(TestData): } assert {prop.name: type(prop) for prop in p.root.properties[0].properties} == expected_types + def test_ordered_refs_object(TestData): with open(TestData / "ordered-refs.json", "r") as file: schema = json.load(file) @@ -109,6 +110,7 @@ def test_ordered_refs_object(TestData): } assert {prop.name: type(prop) for prop in p.root.properties[0].properties} == expected_types + def test_unordered_refs_object(TestData): with open(TestData / "unordered-refs.json", "r") as file: schema = json.load(file) @@ -122,4 +124,3 @@ def test_unordered_refs_object(TestData): "bar": JSFEnum, } assert {prop.name: type(prop) for prop in p.root.properties[0].properties} == expected_types -