{ "info": { "author": "Connor Worley", "author_email": "connorbworley@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "# JSON Schema-powered type annotations\n\nThe goal of this project is to use JSON Schema for type checking in Python.\n\nWhile there is not a perfect 1:1 mapping between concepts in JSON Schema and\nPython's typing system, there is enough of an isomorphism to warrant some\nexploration of the possibilities. Since a JSON document is generally\nrepresented as a ``dict`` in Python programs, this project looks specifically\nat interpreting JSON schema as\n[``TypedDict``](https://www.python.org/dev/peps/pep-0589/) definitions.\n\n**Warning:** there are bound to be some abuses of the [mypy plugin\nsystem](https://mypy.readthedocs.io/en/latest/extending_mypy.html) here. You\nhave been warned.\n\nThis leverages (and is inspired by) https://github.com/Julian/jsonschema.\n\n## Overview\n\nA JSON schema:\n\n```json\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"http://foo.qwerty/some/schema#\",\n \"title\": \"Foo Schema\",\n \"type\": \"object\",\n \"properties\": {\n \"title\": {\n \"type\": \"string\"\n },\n \"awesome\": {\n \"type\": \"number\"\n }\n },\n \"required\": [\"title\"]\n}\n```\n\nA TypedDict:\n\n```python\nfrom jsonschema_typed.types import JSONSchema\n\ndata: JSONSchema['path/to/schema.json'] = dict(title='baz')\ndata['description'] = 'there is no description' # TypedDict \"FooSchema\" has no key 'description'\ndata['awesome'] = 42\ndata['awesome'] = None # Argument 2 has incompatible type \"None\"; expected \"Union[int, float]\"\n```\n\n## Installation\n\n```bash\npip install jsonschema-typed\n```\n\nor\n\n```bash\ngit clone git@github.com:erickpeirson/jsonschema-typed.git\ncd jsonschema-typed\npython setup.py install\n```\n\n## Requirements\n\nSo far I have only tried this with:\n\n- mypy==0.701\n- jsonschema==3.0.1\n\nBut probably older versions work. You could try it out and\n[let me know](https://github.com/erickpeirson/jsonschema-typed/issues).\n\n## Limitations\n\n- ``additionalProperties`` doesn't really have an equivalent in TypedDict. Yet.\n- Cases in which the root of the schema is anything other than an ``object``\n are not terribly interesting for this project, so we ignore them for now.\n Array values for ``type`` (e.g. ``\"type\": [\"object\", \"boolean\"]``) are\n otherwise supported with ``Union``.\n- The ``default`` keyword is not supported; but see:\n https://github.com/python/mypy/issues/6131.\n- Self-references (e.g. ``\"#\"``) can't really work properly until nested\n forward-references are supported; see\n https://github.com/python/mypy/issues/731.\n\nThere are probably others.\n\n\n## Approach\n\nSo far two approaches are attempted:\n\n1. Annotating a ``dict`` instance that will be a ``TypedDict`` that conforms to\n the JSON Schema (as best we can enforce it).\n2. Using a dynamic base class that is typed as a ``TypedDict``.\n\nBoth examples below use the schema:\n\n```json\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"http://foo.qwerty/some/schema#\",\n \"title\": \"Foo Schema\",\n \"type\": \"object\",\n \"properties\": {\n \"title\": {\n \"type\": \"string\"\n },\n \"awesome\": {\n \"type\": \"number\"\n }\n }\n}\n```\n\n\n### First approach: annotating a ``dict``\n\nThis has the advantage of being fairly simple. It is implemented via\n``jsonschema_typed.plugin.JSONSchemaPlugin.get_type_analyze_hook``.\n\n```python\nfrom jsonschema_typed.types import JSONSchema\n\ndata: JSONSchema['path/to/schema.json'] = dict(title='baz')\nreveal_type(data) # Revealed type is 'TypedDict('FooSchema', {'title'?: builtins.str, 'awesome'?: Union[builtins.int, builtins.float]})'\ndata['description'] = 'there is no description' # TypedDict \"FooSchema\" has no key 'description'\ndata['awesome'] = 42\ndata['awesome'] = None # Argument 2 has incompatible type \"None\"; expected \"Union[int, float]\"\n```\n\nHere is the mypy output:\n\n```\nmain.py:4: error: Revealed type is 'TypedDict('FooSchema', {'title'?: builtins.str, 'awesome'?: Union[builtins.int, builtins.float]})'\nmain.py:5: error: TypedDict \"FooSchema\" has no key 'description'\nmain.py:7: error: Argument 2 has incompatible type \"None\"; expected \"Union[int, float]\"\n```\n\nNote that the right-hand side can be a ``dict`` or a subclass of ``dict``, so\nyou could define a subclass like:\n\n```python\nclass Foo(dict):\n \"\"\"Some domain logic on your object.\"\"\"\n\n def do_something(self, arg: int) -> int:\n \"\"\"Do something awesome.\"\"\"\n return arg * self['awesome']\n\ndata: JSONSchema['schema/test.json'] = Foo(title='baz')\nreveal_type(data) # Revealed type is 'TypedDict('FooSchema', {'title'?: builtins.str, 'awesome'?: Union[builtins.int, builtins.float]})'\ndata['description'] = 'there is no description' # TypedDict \"FooSchema\" has no key 'description'\ndata['awesome'] = 42\ndata['awesome'] = None # Argument 2 has incompatible type \"None\"; expected \"Union[int, float]\"\n```\n\nOf course this isn't quite consistent with PEP-589 which\n[states](https://www.python.org/dev/peps/pep-0589/#class-based-syntax) that:\n\n> Methods are not allowed, since the runtime type of a TypedDict object will\n> always be just dict (it is never a subclass of dict).\n\nSo use at your own risk.\n\n### Second approach: dynamic base class\n\nThis has the advantage of being able to add some runtime-functionality, e.g.\nuse ``jsonschema`` to actually validate data at runtime. It is implemented via\n``jsonschema_typed.plugin.JSONSchemaPlugin.get_dynamic_class_hook``.\n\nBut again, this isn't quite consistent with PEP-589 which\n[states](https://www.python.org/dev/peps/pep-0589/#class-based-syntax), so\nuse at your own risk.\n\n```python\nfrom jsonschema_typed.types import JSONSchemaBase\n\n\nBase = JSONSchemaBase('path/to/schema.json')\n\nclass Foo(Base):\n \"\"\"All your base in one place.\"\"\"\n\n def do_something(self, arg: int) -> int:\n \"\"\"Do something awesome.\"\"\"\n return arg * self['awesome']\n\n\ndata = Foo(title='baz')\nreveal_type(data) # Revealed type is 'TypedDict('FooSchema', {'title'?: builtins.str, 'awesome'?: Union[builtins.int, builtins.float]})'\ndata['description'] = 'there is no description' # TypedDict \"FooSchema\" has no key 'description'\ndata['awesome'] = 42\ndata['awesome'] = None # Argument 2 has incompatible type \"None\"; expected \"Union[int, float]\"\n```\n\n## TODO\n\n- [ ] Decide whether to stick with just one approach (and which one)\n- [ ] Write some tests\n- [ ] Test against other versions of mypy + jsonschema", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/connorworley/jsonschema-typed", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "jsonschema-typed2", "package_url": "https://pypi.org/project/jsonschema-typed2/", "platform": "", "project_url": "https://pypi.org/project/jsonschema-typed2/", "project_urls": { "Homepage": "https://github.com/connorworley/jsonschema-typed" }, "release_url": "https://pypi.org/project/jsonschema-typed2/0.3.1/", "requires_dist": null, "requires_python": "~=3.6", "summary": "", "version": "0.3.1" }, "last_serial": 5892908, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "e8920251312eb647e0b2744763b469ae", "sha256": "b2c6fa7d74d09f8193dc99b87e57d578c05dcefb658c2e0b418401a95dec83fa" }, "downloads": -1, "filename": "jsonschema-typed2-0.1.0.tar.gz", "has_sig": false, "md5_digest": "e8920251312eb647e0b2744763b469ae", "packagetype": "sdist", "python_version": "source", "requires_python": "~=3.6", "size": 10219, "upload_time": "2019-09-03T23:06:39", "url": "https://files.pythonhosted.org/packages/14/0f/8d51f35ba965c1ea7e4750d3a7f9f02495228a237f4740a4c7528163842a/jsonschema-typed2-0.1.0.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "d7702761ae610d874e29429da1a9b8f3", "sha256": "d42ed39829c90bd45c33f97b79179227f826c8d96dbcf15bf746b3cf9131d687" }, "downloads": -1, "filename": "jsonschema-typed2-0.2.0.tar.gz", "has_sig": false, "md5_digest": "d7702761ae610d874e29429da1a9b8f3", "packagetype": "sdist", "python_version": "source", "requires_python": "~=3.6", "size": 9678, "upload_time": "2019-09-17T18:39:45", "url": "https://files.pythonhosted.org/packages/82/7e/4f782566539b609995c812ecba1af26cb0c6a727b925cc3557e7117b111b/jsonschema-typed2-0.2.0.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "72f8ed9cc39ea6cbd242f22b86bb8aa3", "sha256": "972a9a3e30e335901864a7b3e60d8a854ab9c5027652dfb015db8dd3f1a2f6fd" }, "downloads": -1, "filename": "jsonschema-typed2-0.3.0.tar.gz", "has_sig": false, "md5_digest": "72f8ed9cc39ea6cbd242f22b86bb8aa3", "packagetype": "sdist", "python_version": "source", "requires_python": "~=3.6", "size": 10593, "upload_time": "2019-09-17T21:38:17", "url": "https://files.pythonhosted.org/packages/53/cd/ac67131f707cdf9beadb519445b93034ec2574b5ce54c436817031b4a9c9/jsonschema-typed2-0.3.0.tar.gz" } ], "0.3.1": [ { "comment_text": "", "digests": { "md5": "8c7f68459b071602dc055dbeeeb5026c", "sha256": "49ddf182427e6fd8e13e57846296b64124279d7cdd026e3fd8a674c932f38bd6" }, "downloads": -1, "filename": "jsonschema-typed2-0.3.1.tar.gz", "has_sig": false, "md5_digest": "8c7f68459b071602dc055dbeeeb5026c", "packagetype": "sdist", "python_version": "source", "requires_python": "~=3.6", "size": 10458, "upload_time": "2019-09-26T21:49:26", "url": "https://files.pythonhosted.org/packages/24/a4/f656fe1d1d07cbc586a5867c26c3fb9cdf1681cde3cd3e3f8ccf7d065233/jsonschema-typed2-0.3.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8c7f68459b071602dc055dbeeeb5026c", "sha256": "49ddf182427e6fd8e13e57846296b64124279d7cdd026e3fd8a674c932f38bd6" }, "downloads": -1, "filename": "jsonschema-typed2-0.3.1.tar.gz", "has_sig": false, "md5_digest": "8c7f68459b071602dc055dbeeeb5026c", "packagetype": "sdist", "python_version": "source", "requires_python": "~=3.6", "size": 10458, "upload_time": "2019-09-26T21:49:26", "url": "https://files.pythonhosted.org/packages/24/a4/f656fe1d1d07cbc586a5867c26c3fb9cdf1681cde3cd3e3f8ccf7d065233/jsonschema-typed2-0.3.1.tar.gz" } ] }