From 279f4550d167560434a47eefec155b5c8e29bf62 Mon Sep 17 00:00:00 2001 From: Petter Friberg Date: Thu, 6 Jun 2024 10:46:35 +0200 Subject: [PATCH 1/3] Tests: Migrate `db/test_transaction.yml` to 'assert_type' tests --- django-stubs/db/transaction.pyi | 2 +- tests/assert_type/db/test_transaction.py | 33 ++++++++++++++++++++++++ tests/typecheck/db/test_transaction.yml | 29 --------------------- 3 files changed, 34 insertions(+), 30 deletions(-) create mode 100644 tests/assert_type/db/test_transaction.py delete mode 100644 tests/typecheck/db/test_transaction.yml diff --git a/django-stubs/db/transaction.pyi b/django-stubs/db/transaction.pyi index 868b8b508..2d5087e4f 100644 --- a/django-stubs/db/transaction.pyi +++ b/django-stubs/db/transaction.pyi @@ -22,7 +22,7 @@ def set_rollback(rollback: bool, using: str | None = ...) -> None: ... def mark_for_rollback_on_error(using: str | None = ...) -> Iterator[None]: ... def on_commit(func: Callable[[], object], using: str | None = ..., robust: bool = ...) -> None: ... -_C = TypeVar("_C", bound=Callable) # Any callable +_C = TypeVar("_C", bound=Callable[..., Any]) # Any callable # Don't inherit from ContextDecorator, so we can provide a more specific signature for __call__ class Atomic: diff --git a/tests/assert_type/db/test_transaction.py b/tests/assert_type/db/test_transaction.py new file mode 100644 index 000000000..e90dd5140 --- /dev/null +++ b/tests/assert_type/db/test_transaction.py @@ -0,0 +1,33 @@ +from collections.abc import Callable + +from django.db.transaction import atomic, non_atomic_requests +from django.http import HttpRequest, HttpResponse +from typing_extensions import assert_type + + +@atomic +def func1(x: int, /) -> list[int]: ... + + +assert_type(func1, Callable[[int], list[int]]) + + +@atomic(using="bla", savepoint=False) +def func2(x: int, /) -> list[int]: ... + + +assert_type(func2, Callable[[int], list[int]]) + + +@non_atomic_requests +def view_func1(request: HttpRequest, /) -> HttpResponse: ... + + +assert_type(view_func1, Callable[[HttpRequest], HttpResponse]) + + +@non_atomic_requests +def view_func2(request: HttpRequest, arg: str, /) -> HttpResponse: ... + + +assert_type(view_func2, Callable[[HttpRequest, str], HttpResponse]) diff --git a/tests/typecheck/db/test_transaction.yml b/tests/typecheck/db/test_transaction.yml deleted file mode 100644 index 77b3989b3..000000000 --- a/tests/typecheck/db/test_transaction.yml +++ /dev/null @@ -1,29 +0,0 @@ -- case: atomic_bare - main: | - from django.db.transaction import atomic - @atomic - def func(x: int) -> list: ... - reveal_type(func) # N: Revealed type is "def (x: builtins.int) -> builtins.list[Any]" -- case: atomic_args - main: | - from django.db.transaction import atomic - @atomic(using='bla', savepoint=False) - def func(x: int) -> list: ... - reveal_type(func) # N: Revealed type is "def (x: builtins.int) -> builtins.list[Any]" -- case: non_atomic_requests_bare - main: | - from typing import Any - from django.db.transaction import non_atomic_requests - from django.http import HttpRequest, HttpResponse - @non_atomic_requests - def view_func(request: HttpRequest) -> HttpResponse: ... - reveal_type(view_func) # N: Revealed type is "def (request: django.http.request.HttpRequest) -> django.http.response.HttpResponse" - -- case: non_atomic_requests_args - main: | - from django.http.request import HttpRequest - from django.http.response import HttpResponse - from django.db.transaction import non_atomic_requests - @non_atomic_requests - def view_func(request: HttpRequest, arg: str) -> HttpResponse: ... - reveal_type(view_func) # N: Revealed type is "def (request: django.http.request.HttpRequest, arg: builtins.str) -> django.http.response.HttpResponse" From 0f835788e03f0c09db6faebda9218c5ce5cd447c Mon Sep 17 00:00:00 2001 From: Petter Friberg Date: Thu, 6 Jun 2024 11:06:47 +0200 Subject: [PATCH 2/3] fixup! Tests: Migrate `db/test_transaction.yml` to 'assert_type' tests --- tests/assert_type/db/test_transaction.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/assert_type/db/test_transaction.py b/tests/assert_type/db/test_transaction.py index e90dd5140..2a4078b97 100644 --- a/tests/assert_type/db/test_transaction.py +++ b/tests/assert_type/db/test_transaction.py @@ -1,33 +1,33 @@ from collections.abc import Callable +from typing import List from django.db.transaction import atomic, non_atomic_requests from django.http import HttpRequest, HttpResponse -from typing_extensions import assert_type @atomic -def func1(x: int, /) -> list[int]: ... +def func1(x: int) -> List[int]: ... -assert_type(func1, Callable[[int], list[int]]) +x1: Callable[[int], List[int]] = func1 @atomic(using="bla", savepoint=False) -def func2(x: int, /) -> list[int]: ... +def func2(x: int) -> List[int]: ... -assert_type(func2, Callable[[int], list[int]]) +x1 = func2 @non_atomic_requests -def view_func1(request: HttpRequest, /) -> HttpResponse: ... +def view_func1(request: HttpRequest) -> HttpResponse: ... -assert_type(view_func1, Callable[[HttpRequest], HttpResponse]) +x2: Callable[[HttpRequest], HttpResponse] = view_func1 @non_atomic_requests -def view_func2(request: HttpRequest, arg: str, /) -> HttpResponse: ... +def view_func2(request: HttpRequest, arg: str) -> HttpResponse: ... -assert_type(view_func2, Callable[[HttpRequest, str], HttpResponse]) +x3: Callable[[HttpRequest, str], HttpResponse] = view_func2 From fffe13fdd444161fb86e30c3bc5fa0414d89a9ac Mon Sep 17 00:00:00 2001 From: Petter Friberg Date: Fri, 7 Jun 2024 10:09:45 +0200 Subject: [PATCH 3/3] fixup! fixup! Tests: Migrate `db/test_transaction.yml` to 'assert_type' tests --- tests/assert_type/db/test_transaction.py | 44 +++++++++++++++++++++-- tests/typecheck/test_helpers.yml | 46 ------------------------ 2 files changed, 42 insertions(+), 48 deletions(-) diff --git a/tests/assert_type/db/test_transaction.py b/tests/assert_type/db/test_transaction.py index 2a4078b97..b3682485c 100644 --- a/tests/assert_type/db/test_transaction.py +++ b/tests/assert_type/db/test_transaction.py @@ -10,6 +10,7 @@ def func1(x: int) -> List[int]: ... x1: Callable[[int], List[int]] = func1 +func1("str") # type: ignore[arg-type] # pyright: ignore[reportArgumentType] @atomic(using="bla", savepoint=False) @@ -17,17 +18,56 @@ def func2(x: int) -> List[int]: ... x1 = func2 +func2("str") # type: ignore[arg-type] # pyright: ignore[reportArgumentType] + + +@atomic() +def func3(param1: str, param2: int) -> bool: ... + + +x2: Callable[[str, int], bool] = func3 +func3(1.0, 2.0) # type: ignore[arg-type] # pyright: ignore[reportArgumentType] + + +class ClassWithAtomicMethod: + @atomic + def method1(self, abc: int) -> str: ... + @atomic(savepoint=True) + def method2(self) -> None: ... + @atomic(using="db", savepoint=True) + def method3(self, myparam: str) -> int: ... + + +# E: Argument 1 to "atomic_method1" of "ClassWithAtomicMethod" has incompatible type "str"; expected "int" +ClassWithAtomicMethod().method1("abc") # type: ignore[arg-type] # pyright: ignore[reportArgumentType] + +x3: Callable[[int], str] = ClassWithAtomicMethod().method1 +ClassWithAtomicMethod().method1(1.0) # type: ignore[arg-type] # pyright: ignore[reportArgumentType] + +x4: Callable[[], None] = ClassWithAtomicMethod().method2 +ClassWithAtomicMethod().method2(1.0) # type: ignore[call-arg] # pyright: ignore[reportCallIssue] + +x5: Callable[[str], int] = ClassWithAtomicMethod().method3 +ClassWithAtomicMethod().method3(1.0) # type: ignore[arg-type] # pyright: ignore[reportArgumentType] + + +with atomic(): + ... +with atomic(using="mydb", savepoint=False, durable=True): + ... @non_atomic_requests def view_func1(request: HttpRequest) -> HttpResponse: ... -x2: Callable[[HttpRequest], HttpResponse] = view_func1 +x6: Callable[[HttpRequest], HttpResponse] = view_func1 +view_func1(1) # type: ignore[arg-type] # pyright: ignore[reportArgumentType] @non_atomic_requests def view_func2(request: HttpRequest, arg: str) -> HttpResponse: ... -x3: Callable[[HttpRequest, str], HttpResponse] = view_func2 +x7: Callable[[HttpRequest, str], HttpResponse] = view_func2 +view_func2(1, 1.0) # type: ignore[arg-type] # pyright: ignore[reportArgumentType] diff --git a/tests/typecheck/test_helpers.yml b/tests/typecheck/test_helpers.yml index 657cf273f..72215bc92 100644 --- a/tests/typecheck/test_helpers.yml +++ b/tests/typecheck/test_helpers.yml @@ -1,49 +1,3 @@ -- case: transaction_atomic_contextmanager - main: | - from django.db import transaction - with transaction.atomic(): - pass - with transaction.atomic(using="mydb"): - pass - with transaction.atomic(using="mydb", savepoint=False): - pass - with transaction.atomic(using="mydb", savepoint=False, durable=True): - pass - -- case: transaction_atomic_decorator - main: | - from django.db import transaction - - @transaction.atomic() - def decorated_func(param1: str, param2: int) -> bool: - pass - # Ensure that the function's type is preserved - reveal_type(decorated_func) # N: Revealed type is "def (param1: builtins.str, param2: builtins.int) -> builtins.bool" - - @transaction.atomic(using="mydb") - def decorated_func_using(param1: str, param2: int) -> bool: - pass - # Ensure that the function's type is preserved - reveal_type(decorated_func_using) # N: Revealed type is "def (param1: builtins.str, param2: builtins.int) -> builtins.bool" - - class ClassWithAtomicMethod: - # Bare decorator - @transaction.atomic - def atomic_method1(self, abc: int) -> str: - pass - @transaction.atomic(savepoint=True) - def atomic_method2(self) -> None: - pass - @transaction.atomic(using="db", savepoint=True) - def atomic_method3(self, myparam: str) -> int: - pass - ClassWithAtomicMethod().atomic_method1("abc") # E: Argument 1 to "atomic_method1" of "ClassWithAtomicMethod" has incompatible type "str"; expected "int" [arg-type] - # Ensure that the method's type is preserved - reveal_type(ClassWithAtomicMethod().atomic_method1) # N: Revealed type is "def (abc: builtins.int) -> builtins.str" - # Ensure that the method's type is preserved - reveal_type(ClassWithAtomicMethod().atomic_method3) # N: Revealed type is "def (myparam: builtins.str) -> builtins.int" - - - case: mark_safe_decorator_and_function main: | from django.utils.safestring import mark_safe