2019-07-16T22:41:44 Created temporary directory: /tmp/pip-ephem-wheel-cache-x8u9335f 2019-07-16T22:41:44 Created temporary directory: /tmp/pip-req-tracker-sdp27y9b 2019-07-16T22:41:44 Created requirements tracker '/tmp/pip-req-tracker-sdp27y9b' 2019-07-16T22:41:44 Created temporary directory: /tmp/pip-wheel-3o8c2wh_ 2019-07-16T22:41:44 Collecting jsonschema-typed==0.1.0 2019-07-16T22:41:44 1 location(s) to search for versions of jsonschema-typed: 2019-07-16T22:41:44 * https://pypi.org/simple/jsonschema-typed/ 2019-07-16T22:41:44 Getting page https://pypi.org/simple/jsonschema-typed/ 2019-07-16T22:41:44 Analyzing links from page https://pypi.org/simple/jsonschema-typed/ 2019-07-16T22:41:44 Found link https://files.pythonhosted.org/packages/a1/1e/2eb6d612341ba74b9ffb945591430bbb70179da8b71d1ac28b2f10e543fb/jsonschema-typed-0.1.0.tar.gz#sha256=b0a04ba1571563deaa54423c8b8be1a08db99735df1598b7ed3dffb10a5c49a7 (from https://pypi.org/simple/jsonschema-typed/) (requires-python:~=3.6), version: 0.1.0 2019-07-16T22:41:44 Using version 0.1.0 (newest of versions: 0.1.0) 2019-07-16T22:41:44 Created temporary directory: /tmp/pip-unpack-et3_ol_q 2019-07-16T22:41:44 Downloading https://files.pythonhosted.org/packages/a1/1e/2eb6d612341ba74b9ffb945591430bbb70179da8b71d1ac28b2f10e543fb/jsonschema-typed-0.1.0.tar.gz 2019-07-16T22:41:44 Downloading from URL https://files.pythonhosted.org/packages/a1/1e/2eb6d612341ba74b9ffb945591430bbb70179da8b71d1ac28b2f10e543fb/jsonschema-typed-0.1.0.tar.gz#sha256=b0a04ba1571563deaa54423c8b8be1a08db99735df1598b7ed3dffb10a5c49a7 (from https://pypi.org/simple/jsonschema-typed/) (requires-python:~=3.6) 2019-07-16T22:41:44 Added jsonschema-typed==0.1.0 from https://files.pythonhosted.org/packages/a1/1e/2eb6d612341ba74b9ffb945591430bbb70179da8b71d1ac28b2f10e543fb/jsonschema-typed-0.1.0.tar.gz#sha256=b0a04ba1571563deaa54423c8b8be1a08db99735df1598b7ed3dffb10a5c49a7 to build tracker '/tmp/pip-req-tracker-sdp27y9b' 2019-07-16T22:41:44 Running setup.py (path:/tmp/pip-wheel-3o8c2wh_/jsonschema-typed/setup.py) egg_info for package jsonschema-typed 2019-07-16T22:41:44 Running command python setup.py egg_info 2019-07-16T22:41:46 # JSON Schema-powered type annotations 2019-07-16T22:41:46 The goal of this project is to use JSON Schema for type checking in Python. 2019-07-16T22:41:46 While there is not a perfect 1:1 mapping between concepts in JSON Schema and 2019-07-16T22:41:46 Python's typing system, there is enough of an isomorphism to warrant some 2019-07-16T22:41:46 exploration of the possibilities. Since a JSON document is generally 2019-07-16T22:41:46 represented as a ``dict`` in Python programs, this project looks specifically 2019-07-16T22:41:46 at interpreting JSON schema as 2019-07-16T22:41:46 [``TypedDict``](https://www.python.org/dev/peps/pep-0589/) definitions. 2019-07-16T22:41:46 **Warning:** there are bound to be some abuses of the [mypy plugin 2019-07-16T22:41:46 system](https://mypy.readthedocs.io/en/latest/extending_mypy.html) here. You 2019-07-16T22:41:46 have been warned. 2019-07-16T22:41:46 This leverages (and is inspired by) https://github.com/Julian/jsonschema. 2019-07-16T22:41:46 ## Overview 2019-07-16T22:41:46 A JSON schema: 2019-07-16T22:41:46 ```json 2019-07-16T22:41:46 { 2019-07-16T22:41:46 "$schema": "http://json-schema.org/draft-07/schema#", 2019-07-16T22:41:46 "$id": "http://foo.qwerty/some/schema#", 2019-07-16T22:41:46 "title": "Foo Schema", 2019-07-16T22:41:46 "type": "object", 2019-07-16T22:41:46 "properties": { 2019-07-16T22:41:46 "title": { 2019-07-16T22:41:46 "type": "string" 2019-07-16T22:41:46 }, 2019-07-16T22:41:46 "awesome": { 2019-07-16T22:41:46 "type": "number" 2019-07-16T22:41:46 } 2019-07-16T22:41:46 }, 2019-07-16T22:41:46 "required": ["title"] 2019-07-16T22:41:46 } 2019-07-16T22:41:46 ``` 2019-07-16T22:41:46 A TypedDict: 2019-07-16T22:41:46 ```python 2019-07-16T22:41:46 from jsonschema_typed.types import JSONSchema 2019-07-16T22:41:46 data: JSONSchema['path/to/schema.json'] = dict(title='baz') 2019-07-16T22:41:46 data['description'] = 'there is no description' # TypedDict "FooSchema" has no key 'description' 2019-07-16T22:41:46 data['awesome'] = 42 2019-07-16T22:41:46 data['awesome'] = None # Argument 2 has incompatible type "None"; expected "Union[int, float]" 2019-07-16T22:41:46 ``` 2019-07-16T22:41:46 ## Installation 2019-07-16T22:41:46 ```bash 2019-07-16T22:41:46 pip install jsonschema-typed 2019-07-16T22:41:46 ``` 2019-07-16T22:41:46 or 2019-07-16T22:41:46 ```bash 2019-07-16T22:41:46 git clone git@github.com:erickpeirson/jsonschema-typed.git 2019-07-16T22:41:46 cd jsonschema-typed 2019-07-16T22:41:46 python setup.py install 2019-07-16T22:41:46 ``` 2019-07-16T22:41:46 ## Requirements 2019-07-16T22:41:46 So far I have only tried this with: 2019-07-16T22:41:46 - mypy==0.701 2019-07-16T22:41:46 - jsonschema==3.0.1 2019-07-16T22:41:46 But probably older versions work. You could try it out and 2019-07-16T22:41:46 [let me know](https://github.com/erickpeirson/jsonschema-typed/issues). 2019-07-16T22:41:46 ## Limitations 2019-07-16T22:41:46 - ``additionalProperties`` doesn't really have an equivalent in TypedDict. Yet. 2019-07-16T22:41:46 - Cases in which the root of the schema is anything other than an ``object`` 2019-07-16T22:41:46 are not terribly interesting for this project, so we ignore them for now. 2019-07-16T22:41:46 Array values for ``type`` (e.g. ``"type": ["object", "boolean"]``) are 2019-07-16T22:41:46 otherwise supported with ``Union``. 2019-07-16T22:41:46 - The ``default`` keyword is not supported; but see: 2019-07-16T22:41:46 https://github.com/python/mypy/issues/6131. 2019-07-16T22:41:46 - Self-references (e.g. ``"#"``) can't really work properly until nested 2019-07-16T22:41:46 forward-references are supported; see 2019-07-16T22:41:46 https://github.com/python/mypy/issues/731. 2019-07-16T22:41:46 There are probably others. 2019-07-16T22:41:46 ## Approach 2019-07-16T22:41:46 So far two approaches are attempted: 2019-07-16T22:41:46 1. Annotating a ``dict`` instance that will be a ``TypedDict`` that conforms to 2019-07-16T22:41:46 the JSON Schema (as best we can enforce it). 2019-07-16T22:41:46 2. Using a dynamic base class that is typed as a ``TypedDict``. 2019-07-16T22:41:46 Both examples below use the schema: 2019-07-16T22:41:46 ```json 2019-07-16T22:41:46 { 2019-07-16T22:41:46 "$schema": "http://json-schema.org/draft-07/schema#", 2019-07-16T22:41:46 "$id": "http://foo.qwerty/some/schema#", 2019-07-16T22:41:46 "title": "Foo Schema", 2019-07-16T22:41:46 "type": "object", 2019-07-16T22:41:46 "properties": { 2019-07-16T22:41:46 "title": { 2019-07-16T22:41:46 "type": "string" 2019-07-16T22:41:46 }, 2019-07-16T22:41:46 "awesome": { 2019-07-16T22:41:46 "type": "number" 2019-07-16T22:41:46 } 2019-07-16T22:41:46 } 2019-07-16T22:41:46 } 2019-07-16T22:41:46 ``` 2019-07-16T22:41:46 ### First approach: annotating a ``dict`` 2019-07-16T22:41:46 This has the advantage of being fairly simple. It is implemented via 2019-07-16T22:41:46 ``jsonschema_typed.plugin.JSONSchemaPlugin.get_type_analyze_hook``. 2019-07-16T22:41:46 ```python 2019-07-16T22:41:46 from jsonschema_typed.types import JSONSchema 2019-07-16T22:41:46 data: JSONSchema['path/to/schema.json'] = dict(title='baz') 2019-07-16T22:41:46 reveal_type(data) # Revealed type is 'TypedDict('FooSchema', {'title'?: builtins.str, 'awesome'?: Union[builtins.int, builtins.float]})' 2019-07-16T22:41:46 data['description'] = 'there is no description' # TypedDict "FooSchema" has no key 'description' 2019-07-16T22:41:46 data['awesome'] = 42 2019-07-16T22:41:46 data['awesome'] = None # Argument 2 has incompatible type "None"; expected "Union[int, float]" 2019-07-16T22:41:46 ``` 2019-07-16T22:41:46 Here is the mypy output: 2019-07-16T22:41:46 ``` 2019-07-16T22:41:46 main.py:4: error: Revealed type is 'TypedDict('FooSchema', {'title'?: builtins.str, 'awesome'?: Union[builtins.int, builtins.float]})' 2019-07-16T22:41:46 main.py:5: error: TypedDict "FooSchema" has no key 'description' 2019-07-16T22:41:46 main.py:7: error: Argument 2 has incompatible type "None"; expected "Union[int, float]" 2019-07-16T22:41:46 ``` 2019-07-16T22:41:46 Note that the right-hand side can be a ``dict`` or a subclass of ``dict``, so 2019-07-16T22:41:46 you could define a subclass like: 2019-07-16T22:41:46 ```python 2019-07-16T22:41:46 class Foo(dict): 2019-07-16T22:41:46 """Some domain logic on your object.""" 2019-07-16T22:41:46 def do_something(self, arg: int) -> int: 2019-07-16T22:41:46 """Do something awesome.""" 2019-07-16T22:41:46 return arg * self['awesome'] 2019-07-16T22:41:46 data: JSONSchema['schema/test.json'] = Foo(title='baz') 2019-07-16T22:41:46 reveal_type(data) # Revealed type is 'TypedDict('FooSchema', {'title'?: builtins.str, 'awesome'?: Union[builtins.int, builtins.float]})' 2019-07-16T22:41:46 data['description'] = 'there is no description' # TypedDict "FooSchema" has no key 'description' 2019-07-16T22:41:46 data['awesome'] = 42 2019-07-16T22:41:46 data['awesome'] = None # Argument 2 has incompatible type "None"; expected "Union[int, float]" 2019-07-16T22:41:46 ``` 2019-07-16T22:41:46 Of course this isn't quite consistent with PEP-589 which 2019-07-16T22:41:46 [states](https://www.python.org/dev/peps/pep-0589/#class-based-syntax) that: 2019-07-16T22:41:46 > Methods are not allowed, since the runtime type of a TypedDict object will 2019-07-16T22:41:46 > always be just dict (it is never a subclass of dict). 2019-07-16T22:41:46 So use at your own risk. 2019-07-16T22:41:46 ### Second approach: dynamic base class 2019-07-16T22:41:46 This has the advantage of being able to add some runtime-functionality, e.g. 2019-07-16T22:41:46 use ``jsonschema`` to actually validate data at runtime. It is implemented via 2019-07-16T22:41:46 ``jsonschema_typed.plugin.JSONSchemaPlugin.get_dynamic_class_hook``. 2019-07-16T22:41:46 But again, this isn't quite consistent with PEP-589 which 2019-07-16T22:41:46 [states](https://www.python.org/dev/peps/pep-0589/#class-based-syntax), so 2019-07-16T22:41:46 use at your own risk. 2019-07-16T22:41:46 ```python 2019-07-16T22:41:46 from jsonschema_typed.types import JSONSchemaBase 2019-07-16T22:41:46 Base = JSONSchemaBase('path/to/schema.json') 2019-07-16T22:41:46 class Foo(Base): 2019-07-16T22:41:46 """All your base in one place.""" 2019-07-16T22:41:46 def do_something(self, arg: int) -> int: 2019-07-16T22:41:46 """Do something awesome.""" 2019-07-16T22:41:46 return arg * self['awesome'] 2019-07-16T22:41:46 data = Foo(title='baz') 2019-07-16T22:41:46 reveal_type(data) # Revealed type is 'TypedDict('FooSchema', {'title'?: builtins.str, 'awesome'?: Union[builtins.int, builtins.float]})' 2019-07-16T22:41:46 data['description'] = 'there is no description' # TypedDict "FooSchema" has no key 'description' 2019-07-16T22:41:46 data['awesome'] = 42 2019-07-16T22:41:46 data['awesome'] = None # Argument 2 has incompatible type "None"; expected "Union[int, float]" 2019-07-16T22:41:46 ``` 2019-07-16T22:41:46 ## TODO 2019-07-16T22:41:46 - [ ] Decide whether to stick with just one approach (and which one) 2019-07-16T22:41:46 - [ ] Write some tests 2019-07-16T22:41:46 - [ ] Test against other versions of mypy + jsonschema 2019-07-16T22:41:46 running egg_info 2019-07-16T22:41:46 creating pip-egg-info/jsonschema_typed.egg-info 2019-07-16T22:41:46 writing pip-egg-info/jsonschema_typed.egg-info/PKG-INFO 2019-07-16T22:41:46 writing dependency_links to pip-egg-info/jsonschema_typed.egg-info/dependency_links.txt 2019-07-16T22:41:46 writing requirements to pip-egg-info/jsonschema_typed.egg-info/requires.txt 2019-07-16T22:41:46 writing top-level names to pip-egg-info/jsonschema_typed.egg-info/top_level.txt 2019-07-16T22:41:46 writing manifest file 'pip-egg-info/jsonschema_typed.egg-info/SOURCES.txt' 2019-07-16T22:41:46 reading manifest file 'pip-egg-info/jsonschema_typed.egg-info/SOURCES.txt' 2019-07-16T22:41:46 writing manifest file 'pip-egg-info/jsonschema_typed.egg-info/SOURCES.txt' 2019-07-16T22:41:46 Source in /tmp/pip-wheel-3o8c2wh_/jsonschema-typed has version 0.1.0, which satisfies requirement jsonschema-typed==0.1.0 from https://files.pythonhosted.org/packages/a1/1e/2eb6d612341ba74b9ffb945591430bbb70179da8b71d1ac28b2f10e543fb/jsonschema-typed-0.1.0.tar.gz#sha256=b0a04ba1571563deaa54423c8b8be1a08db99735df1598b7ed3dffb10a5c49a7 2019-07-16T22:41:46 Removed jsonschema-typed==0.1.0 from https://files.pythonhosted.org/packages/a1/1e/2eb6d612341ba74b9ffb945591430bbb70179da8b71d1ac28b2f10e543fb/jsonschema-typed-0.1.0.tar.gz#sha256=b0a04ba1571563deaa54423c8b8be1a08db99735df1598b7ed3dffb10a5c49a7 from build tracker '/tmp/pip-req-tracker-sdp27y9b' 2019-07-16T22:41:46 Building wheels for collected packages: jsonschema-typed 2019-07-16T22:41:46 Created temporary directory: /tmp/pip-wheel-il0uer_e 2019-07-16T22:41:46 Building wheel for jsonschema-typed (setup.py): started 2019-07-16T22:41:46 Destination directory: /tmp/pip-wheel-il0uer_e 2019-07-16T22:41:46 Running command /usr/bin/python3 -u -c 'import setuptools, tokenize;__file__='"'"'/tmp/pip-wheel-3o8c2wh_/jsonschema-typed/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d /tmp/pip-wheel-il0uer_e 2019-07-16T22:41:48 # JSON Schema-powered type annotations 2019-07-16T22:41:48 The goal of this project is to use JSON Schema for type checking in Python. 2019-07-16T22:41:48 While there is not a perfect 1:1 mapping between concepts in JSON Schema and 2019-07-16T22:41:48 Python's typing system, there is enough of an isomorphism to warrant some 2019-07-16T22:41:48 exploration of the possibilities. Since a JSON document is generally 2019-07-16T22:41:48 represented as a ``dict`` in Python programs, this project looks specifically 2019-07-16T22:41:48 at interpreting JSON schema as 2019-07-16T22:41:48 [``TypedDict``](https://www.python.org/dev/peps/pep-0589/) definitions. 2019-07-16T22:41:48 **Warning:** there are bound to be some abuses of the [mypy plugin 2019-07-16T22:41:48 system](https://mypy.readthedocs.io/en/latest/extending_mypy.html) here. You 2019-07-16T22:41:48 have been warned. 2019-07-16T22:41:48 This leverages (and is inspired by) https://github.com/Julian/jsonschema. 2019-07-16T22:41:48 ## Overview 2019-07-16T22:41:48 A JSON schema: 2019-07-16T22:41:48 ```json 2019-07-16T22:41:48 { 2019-07-16T22:41:48 "$schema": "http://json-schema.org/draft-07/schema#", 2019-07-16T22:41:48 "$id": "http://foo.qwerty/some/schema#", 2019-07-16T22:41:48 "title": "Foo Schema", 2019-07-16T22:41:48 "type": "object", 2019-07-16T22:41:48 "properties": { 2019-07-16T22:41:48 "title": { 2019-07-16T22:41:48 "type": "string" 2019-07-16T22:41:48 }, 2019-07-16T22:41:48 "awesome": { 2019-07-16T22:41:48 "type": "number" 2019-07-16T22:41:48 } 2019-07-16T22:41:48 }, 2019-07-16T22:41:48 "required": ["title"] 2019-07-16T22:41:48 } 2019-07-16T22:41:48 ``` 2019-07-16T22:41:48 A TypedDict: 2019-07-16T22:41:48 ```python 2019-07-16T22:41:48 from jsonschema_typed.types import JSONSchema 2019-07-16T22:41:48 data: JSONSchema['path/to/schema.json'] = dict(title='baz') 2019-07-16T22:41:48 data['description'] = 'there is no description' # TypedDict "FooSchema" has no key 'description' 2019-07-16T22:41:48 data['awesome'] = 42 2019-07-16T22:41:48 data['awesome'] = None # Argument 2 has incompatible type "None"; expected "Union[int, float]" 2019-07-16T22:41:48 ``` 2019-07-16T22:41:48 ## Installation 2019-07-16T22:41:48 ```bash 2019-07-16T22:41:48 pip install jsonschema-typed 2019-07-16T22:41:48 ``` 2019-07-16T22:41:48 or 2019-07-16T22:41:48 ```bash 2019-07-16T22:41:48 git clone git@github.com:erickpeirson/jsonschema-typed.git 2019-07-16T22:41:48 cd jsonschema-typed 2019-07-16T22:41:48 python setup.py install 2019-07-16T22:41:48 ``` 2019-07-16T22:41:48 ## Requirements 2019-07-16T22:41:48 So far I have only tried this with: 2019-07-16T22:41:48 - mypy==0.701 2019-07-16T22:41:48 - jsonschema==3.0.1 2019-07-16T22:41:48 But probably older versions work. You could try it out and 2019-07-16T22:41:48 [let me know](https://github.com/erickpeirson/jsonschema-typed/issues). 2019-07-16T22:41:48 ## Limitations 2019-07-16T22:41:48 - ``additionalProperties`` doesn't really have an equivalent in TypedDict. Yet. 2019-07-16T22:41:48 - Cases in which the root of the schema is anything other than an ``object`` 2019-07-16T22:41:48 are not terribly interesting for this project, so we ignore them for now. 2019-07-16T22:41:48 Array values for ``type`` (e.g. ``"type": ["object", "boolean"]``) are 2019-07-16T22:41:48 otherwise supported with ``Union``. 2019-07-16T22:41:48 - The ``default`` keyword is not supported; but see: 2019-07-16T22:41:48 https://github.com/python/mypy/issues/6131. 2019-07-16T22:41:48 - Self-references (e.g. ``"#"``) can't really work properly until nested 2019-07-16T22:41:48 forward-references are supported; see 2019-07-16T22:41:48 https://github.com/python/mypy/issues/731. 2019-07-16T22:41:48 There are probably others. 2019-07-16T22:41:48 ## Approach 2019-07-16T22:41:48 So far two approaches are attempted: 2019-07-16T22:41:48 1. Annotating a ``dict`` instance that will be a ``TypedDict`` that conforms to 2019-07-16T22:41:48 the JSON Schema (as best we can enforce it). 2019-07-16T22:41:48 2. Using a dynamic base class that is typed as a ``TypedDict``. 2019-07-16T22:41:48 Both examples below use the schema: 2019-07-16T22:41:48 ```json 2019-07-16T22:41:48 { 2019-07-16T22:41:48 "$schema": "http://json-schema.org/draft-07/schema#", 2019-07-16T22:41:48 "$id": "http://foo.qwerty/some/schema#", 2019-07-16T22:41:48 "title": "Foo Schema", 2019-07-16T22:41:48 "type": "object", 2019-07-16T22:41:48 "properties": { 2019-07-16T22:41:48 "title": { 2019-07-16T22:41:48 "type": "string" 2019-07-16T22:41:48 }, 2019-07-16T22:41:48 "awesome": { 2019-07-16T22:41:48 "type": "number" 2019-07-16T22:41:48 } 2019-07-16T22:41:48 } 2019-07-16T22:41:48 } 2019-07-16T22:41:48 ``` 2019-07-16T22:41:48 ### First approach: annotating a ``dict`` 2019-07-16T22:41:48 This has the advantage of being fairly simple. It is implemented via 2019-07-16T22:41:48 ``jsonschema_typed.plugin.JSONSchemaPlugin.get_type_analyze_hook``. 2019-07-16T22:41:48 ```python 2019-07-16T22:41:48 from jsonschema_typed.types import JSONSchema 2019-07-16T22:41:48 data: JSONSchema['path/to/schema.json'] = dict(title='baz') 2019-07-16T22:41:48 reveal_type(data) # Revealed type is 'TypedDict('FooSchema', {'title'?: builtins.str, 'awesome'?: Union[builtins.int, builtins.float]})' 2019-07-16T22:41:48 data['description'] = 'there is no description' # TypedDict "FooSchema" has no key 'description' 2019-07-16T22:41:48 data['awesome'] = 42 2019-07-16T22:41:48 data['awesome'] = None # Argument 2 has incompatible type "None"; expected "Union[int, float]" 2019-07-16T22:41:48 ``` 2019-07-16T22:41:48 Here is the mypy output: 2019-07-16T22:41:48 ``` 2019-07-16T22:41:48 main.py:4: error: Revealed type is 'TypedDict('FooSchema', {'title'?: builtins.str, 'awesome'?: Union[builtins.int, builtins.float]})' 2019-07-16T22:41:48 main.py:5: error: TypedDict "FooSchema" has no key 'description' 2019-07-16T22:41:48 main.py:7: error: Argument 2 has incompatible type "None"; expected "Union[int, float]" 2019-07-16T22:41:48 ``` 2019-07-16T22:41:48 Note that the right-hand side can be a ``dict`` or a subclass of ``dict``, so 2019-07-16T22:41:48 you could define a subclass like: 2019-07-16T22:41:48 ```python 2019-07-16T22:41:48 class Foo(dict): 2019-07-16T22:41:48 """Some domain logic on your object.""" 2019-07-16T22:41:48 def do_something(self, arg: int) -> int: 2019-07-16T22:41:48 """Do something awesome.""" 2019-07-16T22:41:48 return arg * self['awesome'] 2019-07-16T22:41:48 data: JSONSchema['schema/test.json'] = Foo(title='baz') 2019-07-16T22:41:48 reveal_type(data) # Revealed type is 'TypedDict('FooSchema', {'title'?: builtins.str, 'awesome'?: Union[builtins.int, builtins.float]})' 2019-07-16T22:41:48 data['description'] = 'there is no description' # TypedDict "FooSchema" has no key 'description' 2019-07-16T22:41:48 data['awesome'] = 42 2019-07-16T22:41:48 data['awesome'] = None # Argument 2 has incompatible type "None"; expected "Union[int, float]" 2019-07-16T22:41:48 ``` 2019-07-16T22:41:48 Of course this isn't quite consistent with PEP-589 which 2019-07-16T22:41:48 [states](https://www.python.org/dev/peps/pep-0589/#class-based-syntax) that: 2019-07-16T22:41:48 > Methods are not allowed, since the runtime type of a TypedDict object will 2019-07-16T22:41:48 > always be just dict (it is never a subclass of dict). 2019-07-16T22:41:48 So use at your own risk. 2019-07-16T22:41:48 ### Second approach: dynamic base class 2019-07-16T22:41:48 This has the advantage of being able to add some runtime-functionality, e.g. 2019-07-16T22:41:48 use ``jsonschema`` to actually validate data at runtime. It is implemented via 2019-07-16T22:41:48 ``jsonschema_typed.plugin.JSONSchemaPlugin.get_dynamic_class_hook``. 2019-07-16T22:41:48 But again, this isn't quite consistent with PEP-589 which 2019-07-16T22:41:48 [states](https://www.python.org/dev/peps/pep-0589/#class-based-syntax), so 2019-07-16T22:41:48 use at your own risk. 2019-07-16T22:41:48 ```python 2019-07-16T22:41:48 from jsonschema_typed.types import JSONSchemaBase 2019-07-16T22:41:48 Base = JSONSchemaBase('path/to/schema.json') 2019-07-16T22:41:48 class Foo(Base): 2019-07-16T22:41:48 """All your base in one place.""" 2019-07-16T22:41:48 def do_something(self, arg: int) -> int: 2019-07-16T22:41:48 """Do something awesome.""" 2019-07-16T22:41:48 return arg * self['awesome'] 2019-07-16T22:41:48 data = Foo(title='baz') 2019-07-16T22:41:48 reveal_type(data) # Revealed type is 'TypedDict('FooSchema', {'title'?: builtins.str, 'awesome'?: Union[builtins.int, builtins.float]})' 2019-07-16T22:41:48 data['description'] = 'there is no description' # TypedDict "FooSchema" has no key 'description' 2019-07-16T22:41:48 data['awesome'] = 42 2019-07-16T22:41:48 data['awesome'] = None # Argument 2 has incompatible type "None"; expected "Union[int, float]" 2019-07-16T22:41:48 ``` 2019-07-16T22:41:48 ## TODO 2019-07-16T22:41:48 - [ ] Decide whether to stick with just one approach (and which one) 2019-07-16T22:41:48 - [ ] Write some tests 2019-07-16T22:41:48 - [ ] Test against other versions of mypy + jsonschema 2019-07-16T22:41:48 running bdist_wheel 2019-07-16T22:41:48 running build 2019-07-16T22:41:48 running build_py 2019-07-16T22:41:48 creating build 2019-07-16T22:41:48 creating build/lib 2019-07-16T22:41:48 creating build/lib/jsonschema_typed 2019-07-16T22:41:48 copying jsonschema_typed/__init__.py -> build/lib/jsonschema_typed 2019-07-16T22:41:48 copying jsonschema_typed/types.py -> build/lib/jsonschema_typed 2019-07-16T22:41:48 copying jsonschema_typed/plugin.py -> build/lib/jsonschema_typed 2019-07-16T22:41:48 installing to build/bdist.linux-armv7l/wheel 2019-07-16T22:41:48 running install 2019-07-16T22:41:48 running install_lib 2019-07-16T22:41:48 creating build/bdist.linux-armv7l 2019-07-16T22:41:48 creating build/bdist.linux-armv7l/wheel 2019-07-16T22:41:48 creating build/bdist.linux-armv7l/wheel/jsonschema_typed 2019-07-16T22:41:48 copying build/lib/jsonschema_typed/__init__.py -> build/bdist.linux-armv7l/wheel/jsonschema_typed 2019-07-16T22:41:48 copying build/lib/jsonschema_typed/types.py -> build/bdist.linux-armv7l/wheel/jsonschema_typed 2019-07-16T22:41:48 copying build/lib/jsonschema_typed/plugin.py -> build/bdist.linux-armv7l/wheel/jsonschema_typed 2019-07-16T22:41:48 running install_egg_info 2019-07-16T22:41:48 running egg_info 2019-07-16T22:41:48 writing jsonschema_typed.egg-info/PKG-INFO 2019-07-16T22:41:48 writing dependency_links to jsonschema_typed.egg-info/dependency_links.txt 2019-07-16T22:41:48 writing requirements to jsonschema_typed.egg-info/requires.txt 2019-07-16T22:41:48 writing top-level names to jsonschema_typed.egg-info/top_level.txt 2019-07-16T22:41:49 reading manifest file 'jsonschema_typed.egg-info/SOURCES.txt' 2019-07-16T22:41:49 writing manifest file 'jsonschema_typed.egg-info/SOURCES.txt' 2019-07-16T22:41:49 Copying jsonschema_typed.egg-info to build/bdist.linux-armv7l/wheel/jsonschema_typed-0.1.0-py3.7.egg-info 2019-07-16T22:41:49 running install_scripts 2019-07-16T22:41:49 creating build/bdist.linux-armv7l/wheel/jsonschema_typed-0.1.0.dist-info/WHEEL 2019-07-16T22:41:49 creating '/tmp/pip-wheel-il0uer_e/jsonschema_typed-0.1.0-py3-none-any.whl' and adding 'build/bdist.linux-armv7l/wheel' to it 2019-07-16T22:41:49 adding 'jsonschema_typed/__init__.py' 2019-07-16T22:41:49 adding 'jsonschema_typed/plugin.py' 2019-07-16T22:41:49 adding 'jsonschema_typed/types.py' 2019-07-16T22:41:49 adding 'jsonschema_typed-0.1.0.dist-info/METADATA' 2019-07-16T22:41:49 adding 'jsonschema_typed-0.1.0.dist-info/WHEEL' 2019-07-16T22:41:49 adding 'jsonschema_typed-0.1.0.dist-info/top_level.txt' 2019-07-16T22:41:49 adding 'jsonschema_typed-0.1.0.dist-info/RECORD' 2019-07-16T22:41:49 removing build/bdist.linux-armv7l/wheel 2019-07-16T22:41:49 Building wheel for jsonschema-typed (setup.py): finished with status 'done' 2019-07-16T22:41:49 Stored in directory: /tmp/tmpxrtfr6eo 2019-07-16T22:41:49 Successfully built jsonschema-typed 2019-07-16T22:41:49 Cleaning up... 2019-07-16T22:41:49 Removing source in /tmp/pip-wheel-3o8c2wh_/jsonschema-typed 2019-07-16T22:41:49 Removed build tracker '/tmp/pip-req-tracker-sdp27y9b'