Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ extend-ignore = [
"E402", # module-import-not-at-top-of-file
"E252", # missing-whitespace-around-parameter-equals
"F541", # f-string-missing-placeholders
"E731", # don't assign lambdas
]

[tool.ruff.lint.per-file-ignores]
Expand Down
79 changes: 79 additions & 0 deletions tests/test_schemalike.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import textwrap

from typing import Callable, Literal

from typemap.type_eval import eval_typing
from typemap.typing import (
NewProtocol,
Iter,
Attrs,
GetType,
GetName,
Member,
Param,
StrConcat,
)

from . import format_helper


class Schema:
pass


class Type:
pass


class Expression:
pass


# hmmmm... recursion with this sort of thing will be funny...
# how will we handle the decorators or __init_subclass__ or what have you


class Property:
name: str
required: bool
multi: bool
typ: Type
expr: Expression | None


type Schemaify[T] = NewProtocol[
*[p for p in Iter[Attrs[T]]],
*[
Member[
StrConcat[Literal["get_"], GetName[p]],
Callable[
[
Param[Literal["self"], Schemaify[T]],
Param[Literal["schema"], Schema, Literal["keyword"]],
],
GetType[p],
],
Literal["ClassVar"],
]
for p in Iter[Attrs[T]]
],
]


def test_schema_like_1():
tgt = eval_typing(Schemaify[Property])
fmt = format_helper.format_class(tgt)

assert fmt == textwrap.dedent("""\
class Schemaify[tests.test_schemalike.Property]:
name: str
required: bool
multi: bool
typ: tests.test_schemalike.Type
expr: tests.test_schemalike.Expression | None
def get_name(self: Schemaify[tests.test_schemalike.Property], *, schema: tests.test_schemalike.Schema) -> str: ...
def get_required(self: Schemaify[tests.test_schemalike.Property], *, schema: tests.test_schemalike.Schema) -> bool: ...
def get_multi(self: Schemaify[tests.test_schemalike.Property], *, schema: tests.test_schemalike.Schema) -> bool: ...
def get_typ(self: Schemaify[tests.test_schemalike.Property], *, schema: tests.test_schemalike.Schema) -> tests.test_schemalike.Type: ...
def get_expr(self: Schemaify[tests.test_schemalike.Property], *, schema: tests.test_schemalike.Schema) -> tests.test_schemalike.Expression | None: ...
""")
28 changes: 23 additions & 5 deletions tests/test_type_dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ def sbase[Z](a: OrGotcha[T] | Z | None, b: K) -> dict[str, T | Z]:
pass


class CMethod:
@classmethod
def cbase2(cls, lol: int, /, a: bool | None) -> int:
pass


class Wrapper[X](Base[X], AnotherBase[X]):
x: "Wrapper[X | None]"

Expand Down Expand Up @@ -185,7 +191,7 @@ def test_type_dir_link_2():
assert loop is Foo


def test_type_dir_1():
def test_type_dir_1a():
d = eval_typing(Final)

assert format_helper.format_class(d) == textwrap.dedent("""\
Expand All @@ -197,15 +203,25 @@ class Final:
fin: typing.Final[int]
x: tests.test_type_dir.Wrapper[int | None]
ordinary: str
def foo(self, a: int | None, *, b: int = 0) -> dict[str, int]: ...
def base[Z](self, a: int | Z | None, b: ~K) -> dict[str, int | Z]: ...
def foo(self: tests.test_type_dir.Base[int], a: int | None, *, b: int = ...) -> dict[str, int]: ...
def base[Z](self: tests.test_type_dir.Base[int], a: int | Z | None, b: ~K) -> dict[str, int | Z]: ...
@classmethod
def cbase(cls, a: int | None, b: ~K) -> dict[str, int]: ...
def cbase(cls: type[tests.test_type_dir.Base[int]], a: int | None, b: ~K) -> dict[str, int]: ...
@staticmethod
def sbase[Z](a: int | Literal['gotcha!'] | Z | None, b: ~K) -> dict[str, int | Z]: ...
""")


def test_type_dir_1b():
d = eval_typing(CMethod)

assert format_helper.format_class(d) == textwrap.dedent("""\
class CMethod:
@classmethod
def cbase2(_arg0: type[tests.test_type_dir.CMethod], _arg1: int, /, a: bool | None) -> int: ...
""")


def test_type_dir_2():
d = eval_typing(OptionalFinal)

Expand Down Expand Up @@ -394,6 +410,8 @@ def test_type_members_func_3():

assert (
str(typ)
# == "\
# staticmethod[tuple[typemap.typing.Param[typing.Literal['a'], int | typing.Literal['gotcha!'] | Z | None, typing.Never], typemap.typing.Param[typing.Literal['b'], ~K, typing.Never]], dict[str, int | Z]]"
== "\
staticmethod[tuple[typemap.typing.Param[typing.Literal['a'], int | typing.Literal['gotcha!'] | Z | None, typing.Never], typemap.typing.Param[typing.Literal['b'], ~K, typing.Never]], dict[str, int | Z]]"
typemap.typing.GenericCallable[tuple[Z], staticmethod[tuple[typemap.typing.Param[typing.Literal['a'], int | typing.Literal['gotcha!'] | Z | None, typing.Never], typemap.typing.Param[typing.Literal['b'], ~K, typing.Never]], dict[str, int | Z]]]"
)
43 changes: 41 additions & 2 deletions tests/test_type_eval.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import pytest

import collections
import textwrap
import unittest
Expand All @@ -15,6 +13,8 @@
Union,
)

import pytest

from typemap.type_eval import eval_typing
from typemap.typing import (
Attrs,
Expand Down Expand Up @@ -730,3 +730,42 @@ def test_eval_literal_idempotent_01():
nt = eval_typing(t)
assert t == nt
t = nt


def test_callable_to_signature():
from typemap.type_eval._eval_operators import _callable_type_to_signature
from typemap.typing import Param

# Test the example from the docstring:
# def func(
# a: int,
# /,
# b: int,
# c: int = 0,
# *args: int,
# d: int,
# e: int = 0,
# **kwargs: int
# ) -> int:
callable_type = Callable[
[
Param[None, int],
Param[Literal["b"], int],
Param[Literal["c"], int, Literal["default"]],
Param[None, int, Literal["*"]],
Param[Literal["d"], int, Literal["keyword"]],
Param[Literal["e"], int, Literal["default", "keyword"]],
Param[None, int, Literal["**"]],
],
int,
]

sig = _callable_type_to_signature(callable_type)

params = list(sig.parameters.values())
assert len(params) == 7

assert str(sig) == (
'(_arg0: int, /, b: int, c: int = ..., *args: int, '
'd: int, e: int = ..., **kwargs: int) -> int'
)
2 changes: 1 addition & 1 deletion typemap/type_eval/_apply_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,4 +396,4 @@ def flatten_class_explicit(obj: typing.Any):
return _flatten_class_explicit(obj, ctx)


flatten_class = flatten_class_explicit
flatten_class = flatten_class_new_proto
Loading