# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['dict_typer']

package_data = \
{'': ['*']}

install_requires = \
['click>=7.1.1,<8.0.0', 'rich>=1.0.0,<2.0.0']

entry_points = \
{'console_scripts': ['dict-typer = dict_typer:cli']}

setup_kwargs = {
    'name': 'dict-typer',
    'version': '0.1.10',
    'description': 'A simple library to take data and turn it into python typing definitions.',
    'long_description': '# Dict-Typer\n\nA simple tool to take a json payload and convert it into Python TypedDict class\ndefinitions\n\n## Why is this useful?\n\nWhen dealing with API responses, you\'re very likely to be using JSON responses,\nand you might have deeply nested dictionaries, lists of items and it can be\nslightly hard to wrap your head around the structure of the responess you are\nworking with. The first thing I usually do it try to create some data structure\naround it so that I can benefit from linting and typing information in my code.\n\nNow this tends to be time consuming and error prone, so I thought it might be a\ngood idea to automate this process with a tool for the future. Just as an\nexample, if we take the output generated from the Example section below and\nimagine it\'s a response we get from an api. We can plug it in like this:\n\n```python\nfrom project.types import Root\n\n\ndef get_from_api() -> Root:\n    pass\n\n\ndef run() -> None:\n    response = get_from_api()\n\n    test1 = response["nested_dict"]["number"] + 1\n    test2 = response["nested_dict"]["string"] + 1\n    test3 = response["nested_dict"]["non_existant"] + 1\n    for item in response["optional_items"]:\n        print(item + 1)\n```\n\nand if we run mypy on this\n\n```shell\n-> % poetry run mypy test.py\ntest.py:43: error: Unsupported operand types for + ("str" and "int")\ntest.py:44: error: TypedDict "NestedDict" has no key \'non_existant\'\ntest.py:46: error: Unsupported operand types for + ("None" and "int")\ntest.py:46: error: Unsupported operand types for + ("str" and "int")\ntest.py:46: note: Left operand is of type "Union[None, int, str]"\nFound 4 errors in 1 file (checked 1 source file)\n```\n\nit will immediately detect four issues!\n\nI also want to use this project to learn more about analysing code, making sure\nthe project is well tested so that it\'s easy to experiment and try different\napproaches.\n\n## Usage\n\nEither supply a path to a file or pipe json output to `dict-typer`\n\n```help\n-> % dict-typer --help\n\nUsage: dict-typer [OPTIONS] [FILE]...\n\nOptions:\n  --imports / --no-imports  Show imports at the top, default: True\n  -r, --rich                Show rich output.\n  -l, --line-numbers        Show line numbers if rich.\n  --version                 Show the version and exit.\n  --help                    Show this message and exit.\n\n-> % dict-typer ./.example.json\n...\n\n-> % curl example.com/test.json | dict-typer\n...\n```\n\n## TypeDict definitions\n\nThere are two ways to define a TypedDict, the primary one that uses the class\nbased structure, as seen in the examples here. It\'s easier to read, but it has\na limitation that the each key has to be avalid identifier and not a reserved\nkeyword. Normally that\'s not an issue, but if you have for example, the\nfollowing data\n\n```json\n{\n    "numeric-id": 123,\n    "from": "far away",\n}\n```\n\nwhich is valid json, but has the invalid identifier `numeric-id` and reserved\nkeyword `from`, meaning the definition\n\n```python\nclass Root(TypedDict):\n    numeric-id: int\n    from: str\n```\n\nis invalid. In detected cases, dict-typer will use an [alternative\nway](https://www.python.org/dev/peps/pep-0589/#alternative-syntax) to define\nthose types, looking like this\n\n```python\nRoot = TypedDict(\'Root\', {\'numeric-id\': int, \'from\': str\'})\n```\n\nwhich is not as readable, but valid.\n\ndict-typer by default only uses the alternative definition for the types with\ninvalid keys.\n\n## Lists\n\nIf the root of the payload is a list, it will be treated just like a list\nwithin a dictionary, where each item is parsed and definitions created for sub\nitems. In these cases, a type alias is added as well to the output to capture\nthe type of the list. For example, the list `[1, "2", 3.0, { "id": 123 }, {\n"id": 456 }]` will generate the following definitions:\n\n```python\nfrom typing_extensions import TypedDict\n\n\nclass RootItem(TypedDict):\n    id: int\n\nRoot = List[Union[RootItem, float, int, str]]\n```\n\n## Examples\n\n### Calling from shell\n\n```shell\n-> % cat .example.json\n{\n  "number_int": 123,\n  "number_float": 3.0,\n  "string": "string",\n  "list_single_type": ["a", "b", "c"],\n  "list_mixed_type": ["1", 2, 3.0],\n  "nested_dict": {\n    "number": 1,\n    "string": "value"\n  },\n  "same_nested_dict": {\n    "number": 2,\n    "string": "different value"\n  },\n  "multipe_levels": {\n    "level2": {\n      "level3": {\n        "number": 3,\n        "string": "more values"\n      }\n    }\n  },\n  "nested_invalid": { "numeric-id": 123, "from": "far away" },\n  "optional_items": [1, 2, "3", "4", null, 5, 6, null]\n}\n\n-> % cat .example.json | dict-typer\nfrom typing import List, Union\n\nfrom typing_extensions import TypedDict\n\n\nclass NestedDict(TypedDict):\n    number: int\n    string: str\n\n\nclass Level2(TypedDict):\n    level3: NestedDict\n\n\nclass MultipeLevels(TypedDict):\n    level2: Level2\n\n\nNestedInvalid = TypedDict("NestedInvalid", {\n    "numeric-id": int,\n    "from": str,\n})\n\n\nclass Root(TypedDict):\n    number_int: int\n    number_float: float\n    string: str\n    list_single_type: List[str]\n    list_mixed_type: List[Union[float, int, str]]\n    nested_dict: NestedDict\n    same_nested_dict: NestedDict\n    multipe_levels: MultipeLevels\n    nested_invalid: NestedInvalid\n    optional_items: List[Union[None, int, str]]\n```\n\n### Calling from Python\n```python\nIn [1]: source = {\n   ...:   "number_int": 123,\n   ...:   "number_float": 3.0,\n   ...:   "string": "string",\n   ...:   "list_single_type": ["a", "b", "c"],\n   ...:   "list_mixed_type": ["1", 2, 3.0],\n   ...:   "nested_dict": {\n   ...:     "number": 1,\n   ...:     "string": "value"\n   ...:   },\n   ...:   "same_nested_dict": {\n   ...:     "number": 2,\n   ...:     "string": "different value"\n   ...:   },\n   ...:   "multipe_levels": {\n   ...:     "level2": {\n   ...:       "level3": {\n   ...:         "number": 3,\n   ...:         "string": "more values"\n   ...:       }\n   ...:     }\n   ...:   },\n   ...:   "nested_invalid": { "numeric-id": 123, "from": "far away" },\n   ...:   "optional_items": [1, 2, "3", "4", None, 5, 6, None]\n   ...: }\n   ...:\n\nIn [2]: from dict_typer import get_type_definitions\n\nIn [3]: print(get_type_definitions(source, show_imports=True))\nfrom typing import List, Union\n\nfrom typing_extensions import TypedDict\n\n\nclass NestedDict(TypedDict):\n    number: int\n    string: str\n\n\nclass Level2(TypedDict):\n    level3: NestedDict\n\n\nclass MultipeLevels(TypedDict):\n    level2: Level2\n\n\nNestedInvalid = TypedDict("NestedInvalid", {\n    "numeric-id": int,\n    "from": str,\n})\n\n\nclass Root(TypedDict):\n    number_int: int\n    number_float: float\n    string: str\n    list_single_type: List[str]\n    list_mixed_type: List[Union[float, int, str]]\n    nested_dict: NestedDict\n    same_nested_dict: NestedDict\n    multipe_levels: MultipeLevels\n    nested_invalid: NestedInvalid\n    optional_items: List[Union[None, int, str]]\n```\n',
    'author': 'Axel',
    'author_email': 'dict.typer@absalon.is',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/ikornaselur/dict-typer',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'entry_points': entry_points,
    'python_requires': '>=3.6,<4.0',
}


setup(**setup_kwargs)
