def test_convert_with_multiple_levels_nested_dict() -> None:
    source = {"level1": {"level2": {"level3": {"level4": "foo"}}}}

    # fmt: off
    expected = "\n".join([
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Level3(TypedDict):",
        "    level4: str",
        "",
        "",
        "class Level2(TypedDict):",
        "    level3: Level3",
        "",
        "",
        "class Level1(TypedDict):",
        "    level2: Level2",
        "",
        "",
        "class Root(TypedDict):",
        "    level1: Level1",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
Beispiel #2
0
def test_convert_optional_combined_dicts() -> None:
    source = {
        "owner": {"name": "foo", "age": 44},
        "coOwner": {"name": "bar", "age": None},
    }

    # fmt: off
    expected = "\n".join([
        "from typing import Optional",
        "",
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Owner(TypedDict):",
        "    name: str",
        "    age: Optional[int]",
        "",
        "",
        "class Root(TypedDict):",
        "    owner: Owner",
        "    coOwner: Owner",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_convert_with_repeated_nested_dict() -> None:
    source = {
        "nest": {
            "foo": "bar"
        },
        "other_nest": {
            "foo": "qux"
        },
        "unique_nest": {
            "baz": "qux"
        },
    }

    # fmt: off
    expected = "\n".join([
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Nest(TypedDict):",
        "    foo: str",
        "",
        "",
        "class UniqueNest(TypedDict):",
        "    baz: str",
        "",
        "",
        "class Root(TypedDict):",
        "    nest: Nest",
        "    other_nest: Nest",
        "    unique_nest: UniqueNest",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_snapshots(snapshot: Any, fixture: str) -> None:
    with open(f"tests/snapshot/fixtures/{fixture}.json", "r") as f:
        source = json.load(f)

    output = get_type_definitions(source)

    snapshot.assert_match(output)
def test_convert_with_invalid_key_names_nested() -> None:
    source = {"invalid-key": {"id": 123}}

    # fmt: off
    expected = "\n".join([
        "from typing_extensions import TypedDict", "", "",
        "class InvalidKey(TypedDict):", "    id: int", "", "",
        'Root = TypedDict("Root", {', '    "invalid-key": InvalidKey,', '})'
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_convert_with_invalid_key_names() -> None:
    source = {"invalid-key": 123, "from": "far away"}

    # fmt: off
    expected = "\n".join([
        "from typing_extensions import TypedDict", "", "",
        'Root = TypedDict("Root", {', '    "invalid-key": int,',
        '    "from": str,', '})'
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_convert_imports_with_no_typed_dict() -> None:
    source = [1, 2, 3]

    # fmt: off
    expected = "\n".join([
        "from typing import List",
        "",
        "",
        "Root = List[int]",
    ])
    # fmt: on

    assert get_type_definitions(source, show_imports=True) == expected
Beispiel #8
0
def test_convert_not_optional_if_multiple_types_with_none() -> None:
    source = [1, 2, None, 3.0, 4.0, None, 5, 6]

    # fmt: off
    expected = "\n".join([
        "from typing import List, Union",
        "",
        "",
        "Root = List[Union[None, float, int]]",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
Beispiel #9
0
def test_convert_empty_root_list() -> None:
    source: List = []

    # fmt: off
    expected = "\n".join([
        "from typing import List",
        "",
        "",
        "Root = List",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
Beispiel #10
0
def test_convert_single_optional_in_list() -> None:
    source = [1, 2, None, 3, 4, None, 5, 6]

    # fmt: off
    expected = "\n".join([
        "from typing import List, Optional",
        "",
        "",
        "Root = List[Optional[int]]",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
Beispiel #11
0
def test_script_runs_with_nonzero() -> None:
    """ A python e2e test

    1. Takes a source and converts it to a definition
    2. Adds a simple script to use the Type
    3. Saves the definition output into a Python file
    4. Runs the file to verify it runs
    """

    # 1.
    source_type_name = "Test"
    type_postfix = "Type"

    output = get_type_definitions(
        TEST_SOURCE,
        root_type_name=source_type_name,
        type_postfix=type_postfix,
        show_imports=True,
    )

    # 2.
    input_string = f"test_source: {source_type_name}{type_postfix} = {TEST_SOURCE}"
    # fmt: off
    output += "\n".join([
        "",
        "",
        f"{input_string}",
        "print(test_source)",
    ])
    # fmt: on

    # 3.
    with tempfile.NamedTemporaryFile(suffix=".py") as f:
        f.write(output.encode("utf-8"))
        f.flush()

        # 4.
        with subprocess.Popen(["python", f.name],
                              stdout=subprocess.PIPE) as proc:
            stdout, stderr = proc.communicate()
            assert not proc.returncode, "\n".join([
                "Non zero return code from script.",
                "stderr:",
                stderr and stderr.decode("utf-8") or "",
                "Full script:",
                "-" * 60,
                _line_numbered(output),
                "-" * 60,
            ])

            assert stdout.decode("utf-8") == f"{TEST_SOURCE}\n"
def test_convert_none() -> None:
    source = {"value": None}

    # fmt: off
    expected = "\n".join([
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Root(TypedDict):",
        "    value: None",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_convert_base_types() -> None:
    source = {
        "bool_type": True,
        "bytearray_type": bytearray([102, 111, 111]),
        "bytes_types": b"foo",
        "complex_Type": 1 + 1j,
        "enumerate_type": enumerate(["a", "b", "c"]),
        "float_type": 1.2,
        "int_type": 1,
        "memoryview_type": memoryview(b"foo"),
        "range_type": range(30),
        "str_type": "foo",
        "filter_type": filter(lambda x: x < 10, range(20)),
        "map_type": map(lambda x: x * 2, range(20)),
        "zip_type": zip([1, 2], ["a", "b"]),
        "list_type": [1, 2, 3],
        "tuple_type": (1, 2, 3),
        "set_type": {1, 2, 3},
        "frozenset_type": frozenset([1, 2, 3]),
    }

    # fmt: off
    expected = "\n".join([
        "from typing import FrozenSet, List, Set, Tuple",
        "",
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Root(TypedDict):",
        "    bool_type: bool",
        "    bytearray_type: bytearray",
        "    bytes_types: bytes",
        "    complex_Type: complex",
        "    enumerate_type: enumerate",
        "    float_type: float",
        "    int_type: int",
        "    memoryview_type: memoryview",
        "    range_type: range",
        "    str_type: str",
        "    filter_type: filter",
        "    map_type: map",
        "    zip_type: zip",
        "    list_type: List[int]",
        "    tuple_type: Tuple[int]",
        "    set_type: Set[int]",
        "    frozenset_type: FrozenSet[int]",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_convert_empty_root_dict() -> None:
    source: Dict = {}

    # fmt: off
    expected = "\n".join([
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Root(TypedDict):",
        "    pass",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
Beispiel #15
0
def test_mypy_has_no_issues() -> None:
    """ A mypy e2e test

    1. Takes a source and converts it to a definition
    2. Adds a simple script to use the Type
    3. Saves the definition output into a Python file
    4. Runs mypy on the script to verify there are no typing issues
    """

    # 1.
    source_type_name = "Test"
    type_postfix = "Type"

    output = get_type_definitions(
        TEST_SOURCE,
        root_type_name=source_type_name,
        type_postfix=type_postfix,
        show_imports=True,
    )

    # 2.
    input_string = f"test_source: {source_type_name}{type_postfix} = {TEST_SOURCE}"
    # fmt: off
    output += "\n".join([
        "",
        "",
        f"{input_string}",
        "print(test_source)",
    ])
    # fmt: on

    # 3.
    with tempfile.NamedTemporaryFile(suffix=".py") as f:
        f.write(output.encode("utf-8"))
        f.flush()

        # 4.

        # 4.
        with subprocess.Popen(["mypy", f.name],
                              stdout=subprocess.PIPE) as proc:
            stdout, stderr = proc.communicate()
            assert not proc.returncode, "\n".join([
                "Non zero return code from mypy.", "stderr:",
                stderr.decode("utf-8")
            ])

            assert (stdout.decode("utf-8") ==
                    "Success: no issues found in 1 source file\n")
def test_convert_imports_with_no_typing_imports() -> None:
    source = {"id": 10, "value": "value"}

    # fmt: off
    expected = "\n".join([
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Root(TypedDict):",
        "    id: int",
        "    value: str",
    ])
    # fmt: on

    assert get_type_definitions(source, show_imports=True) == expected
Beispiel #17
0
def test_convert_with_simple_tuple() -> None:
    source = {"items": (1, 2, 3)}

    # fmt: off
    expected = "\n".join([
        "from typing import Tuple",
        "",
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Root(TypedDict):",
        "    items: Tuple[int]",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
Beispiel #18
0
def test_convert_with_mixed_list() -> None:
    source = {"items": [1, "2", 3.5]}

    # fmt: off
    expected = "\n".join([
        "from typing import List, Union",
        "",
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Root(TypedDict):",
        "    items: List[Union[float, int, str]]",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_convert_simple_json() -> None:
    source = {"id": 123, "item": "value", "progress": 0.71}

    # fmt: off
    expected = "\n".join([
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Root(TypedDict):",
        "    id: int",
        "    item: str",
        "    progress: float",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
Beispiel #20
0
def test_convert_with_empty_list() -> None:
    source = {"items": []}  # type: ignore

    # fmt: off
    expected = "\n".join([
        "from typing import List",
        "",
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Root(TypedDict):",
        "    items: List",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_convert_hides_import_optionally() -> None:
    source = {
        "itemsList": [1, 2.0, "3"],
        "itemsTuple": (4, 5, 6),
        "itemsSet": {7, 8, 9.0},
    }

    # fmt: off
    expected = "\n".join([
        "class Root(TypedDict):",
        "    itemsList: List[Union[float, int, str]]",
        "    itemsTuple: Tuple[int]",
        "    itemsSet: Set[Union[float, int]]",
    ])
    # fmt: on

    assert get_type_definitions(source, show_imports=False) == expected
def test_convert_simple_json_forced_alternative() -> None:
    source = {"id": 123, "item": "value", "progress": 0.71}

    # fmt: off
    expected = "\n".join([
        "from typing_extensions import TypedDict",
        "",
        "",
        'Root = TypedDict("Root", {',
        '    "id": int,',
        '    "item": str,',
        '    "progress": float,',
        '})',
    ])
    # fmt: on

    assert expected == get_type_definitions(source, force_alternative=True)
def test_convert_with_nested_dict() -> None:
    source = {"nest": {"foo": "bar"}}

    # fmt: off
    expected = "\n".join([
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Nest(TypedDict):",
        "    foo: str",
        "",
        "",
        "class Root(TypedDict):",
        "    nest: Nest",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_convert_list_of_repeated_dicts() -> None:
    source = {"dictList": [{"id": 123}, {"id": 456}, {"id": 789}]}

    # fmt: off
    expected = "\n".join([
        "from typing import List",
        "",
        "from typing_extensions import TypedDict",
        "",
        "",
        "class DictListItem0(TypedDict):",
        "    id: int",
        "",
        "",
        "class Root(TypedDict):",
        "    dictList: List[DictListItem0]",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_convert_list_of_repeated_dicts_different_types_combined() -> None:
    source = {"dictList": [{"id": 123}, {"id": 456.0}, {"id": "789"}]}

    # fmt: off
    expected = "\n".join([
        "from typing import List, Union",
        "",
        "from typing_extensions import TypedDict",
        "",
        "",
        "class DictListItem0(TypedDict):",
        "    id: Union[float, int, str]",
        "",
        "",
        "class Root(TypedDict):",
        "    dictList: List[DictListItem0]",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_convert_optionally_adds_imports_with_nested_defs() -> None:
    source = {"itemsList": [1, 2, 3], "nest": {"itemsTuple": ({4, 5.0}, 5, 6)}}

    # fmt: off
    expected = "\n".join([
        "from typing import List, Set, Tuple, Union",
        "",
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Nest(TypedDict):",
        "    itemsTuple: Tuple[Union[Set[Union[float, int]], int]]",
        "",
        "",
        "class Root(TypedDict):",
        "    itemsList: List[int]",
        "    nest: Nest",
    ])
    # fmt: on

    assert get_type_definitions(source, show_imports=True) == expected
def test_convert_with_a_name_map() -> None:
    source = {
        "id": 123,
        "item": "value",
        "things": [{
            "foo": "bar"
        }, {
            "baz": "quz"
        }]
    }

    name_map = {
        "Root": "Source",
        "ThingsItem0": "Foo",
        "ThingsItem1": "Baz",
        "NotUser": "******",
    }

    expected = "\n".join([
        "from typing import List, Union",
        "",
        "from typing_extensions import TypedDict",
        "",
        "",
        "class Foo(TypedDict):",
        "    foo: str",
        "",
        "",
        "class Baz(TypedDict):",
        "    baz: str",
        "",
        "",
        "class Source(TypedDict):",
        "    id: int",
        "    item: str",
        "    things: List[Union[Baz, Foo]]",
    ])

    assert expected == get_type_definitions(source, name_map=name_map)
def test_convert_list_of_mixed_dicts() -> None:
    source = {
        "dictList": [{
            "foo": 123
        }, {
            "bar": 456
        }, {
            "baz": 789
        }, {
            "baz": 000
        }]
    }

    # fmt: off
    expected = "\n".join([
        "from typing import List, Union",
        "",
        "from typing_extensions import TypedDict",
        "",
        "",
        "class DictListItem0(TypedDict):",
        "    foo: int",
        "",
        "",
        "class DictListItem1(TypedDict):",
        "    bar: int",
        "",
        "",
        "class DictListItem2(TypedDict):",
        "    baz: int",
        "",
        "",
        "class Root(TypedDict):",
        "    dictList: List[Union[DictListItem0, DictListItem1, DictListItem2]]",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_convert_nested_overlapping_dict() -> None:
    source = [
        {
            "x": {
                "foo": "bar"
            }
        },
        {
            "x": {
                "baz": "qux"
            }
        },
    ]

    # fmt: off
    expected = "\n".join([
        "from typing import List, Union",
        "",
        "from typing_extensions import TypedDict",
        "",
        "",
        "class X(TypedDict):",
        "    foo: str",
        "",
        "",
        "class X1(TypedDict):",
        "    baz: str",
        "",
        "",
        "class RootItem0(TypedDict):",
        "    x: Union[X, X1]",
        "",
        "",
        "Root = List[RootItem0]",
    ])
    # fmt: on

    assert expected == get_type_definitions(source)
def test_convert_base_root_values(source: Source, expected: str) -> None:
    assert get_type_definitions(source) == f"Root = {expected}"