File: test_monkeypatching.py

package info (click to toggle)
python-django-stubs 5.2.9-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,832 kB
  • sloc: python: 5,185; makefile: 15; sh: 8
file content (135 lines) | stat: -rw-r--r-- 3,927 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import builtins
from collections.abc import Iterable
from contextlib import suppress
from typing import Protocol

import pytest
from _pytest.fixtures import FixtureRequest
from _pytest.monkeypatch import MonkeyPatch
from django.db.models import Model
from django.forms.models import ModelForm

import django_stubs_ext
from django_stubs_ext import patch
from django_stubs_ext.patch import _need_generic, _VersionSpec


class _MakeGenericClasses(Protocol):
    """Used to represent a type of ``make_generic_classes`` fixture."""

    def __call__(
        self,
        django_version: _VersionSpec | None = None,
        extra_classes: Iterable[type] | None = None,
        include_builtins: bool = True,
    ) -> None: ...


@pytest.fixture(scope="function")
def make_generic_classes(
    request: FixtureRequest,
    monkeypatch: MonkeyPatch,
) -> _MakeGenericClasses:
    _extra_classes: list[type] = []

    def fin() -> None:
        for el in _need_generic:
            with suppress(AttributeError):
                delattr(el.cls, "__class_getitem__")
        for cls in _extra_classes:
            with suppress(AttributeError):
                delattr(cls, "__class_getitem__")
        _extra_classes.clear()
        with suppress(AttributeError):
            del builtins.reveal_type
        with suppress(AttributeError):
            del builtins.reveal_locals

    def factory(
        django_version: _VersionSpec | None = None,
        extra_classes: Iterable[type] | None = None,
        include_builtins: bool = True,
    ) -> None:
        if extra_classes:
            _extra_classes.extend(extra_classes)
        if django_version is not None:
            monkeypatch.setattr(patch, "VERSION", django_version)
        django_stubs_ext.monkeypatch(extra_classes=extra_classes, include_builtins=include_builtins)

    request.addfinalizer(fin)
    return factory


def test_patched_generics(make_generic_classes: _MakeGenericClasses) -> None:
    """Test that the generics actually get patched."""
    make_generic_classes()

    for el in _need_generic:
        if el.version is None:
            assert el.cls[type] is el.cls  # `type` is arbitrary

    class TestForm(ModelForm[Model]):
        pass


def test_patched_extra_classes_generics(make_generic_classes: _MakeGenericClasses) -> None:
    """Test that the generics actually get patched for extra classes."""

    class _NotGeneric:
        pass

    extra_classes = [_NotGeneric]
    make_generic_classes(django_version=None, extra_classes=extra_classes)

    for cls in extra_classes:
        assert cls[type] is cls  # type: ignore[misc]

    class _TestGeneric(_NotGeneric[Model]):  # type: ignore[type-arg]
        pass


@pytest.mark.parametrize(
    "django_version",
    [
        (2, 2),
        (3, 0),
        (3, 1),
        (3, 2),
        (4, 0),
        (4, 1),
    ],
)
def test_patched_version_specific(
    django_version: _VersionSpec,
    make_generic_classes: _MakeGenericClasses,
) -> None:
    """Test version specific types."""
    make_generic_classes(django_version)

    for el in _need_generic:
        if el.version is not None and django_version <= el.version:
            assert el.cls[int] is el.cls


def test_mypy_builtins_not_patched_globally(
    make_generic_classes: _MakeGenericClasses,
) -> None:
    """Ensures that builtins are not patched with `mypy` specific helpers.

    This should only happen during `django.setup()`
    (https://github.com/typeddjango/django-stubs/issues/609).
    """
    make_generic_classes(include_builtins=False)

    assert not hasattr(builtins, "reveal_type")
    assert not hasattr(builtins, "reveal_locals")


def test_mypy_builtins_patched(
    make_generic_classes: _MakeGenericClasses,
) -> None:
    """Ensures that builtins are patched by default."""
    make_generic_classes()

    assert hasattr(builtins, "reveal_type")
    assert hasattr(builtins, "reveal_locals")