mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-10 14:01:56 +08:00
Fixes CI
This commit is contained in:
@@ -36,11 +36,11 @@ repos:
|
|||||||
language: system
|
language: system
|
||||||
types: [ python ]
|
types: [ python ]
|
||||||
exclude: "scripts/|django_stubs_ext/"
|
exclude: "scripts/|django_stubs_ext/"
|
||||||
args: [ "--cache-dir=/dev/null", "--no-incremental" ]
|
args: [ "--config=mypy.ini", "--cache-dir=/dev/null", "--no-incremental" ]
|
||||||
- id: mypy
|
- id: mypy
|
||||||
name: mypy (django_stubs_ext)
|
name: mypy (django_stubs_ext)
|
||||||
entry: mypy
|
entry: mypy
|
||||||
language: system
|
language: system
|
||||||
types: [ python ]
|
types: [ python ]
|
||||||
files: "django_stubs_ext/|django_stubs_ext/tests/"
|
files: "django_stubs_ext/|django_stubs_ext/tests/"
|
||||||
args: [ "--cache-dir=/dev/null", "--no-incremental", "--strict" ]
|
args: [ "--config=mypy.ini", "--cache-dir=/dev/null", "--no-incremental", "--strict" ]
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -58,7 +58,7 @@ We rely on different `django` and `mypy` versions:
|
|||||||
|
|
||||||
No, it is not. We are independent from Django at the moment.
|
No, it is not. We are independent from Django at the moment.
|
||||||
There's a [proposal](https://github.com/django/deps/pull/65) to merge our project into the Django itself.
|
There's a [proposal](https://github.com/django/deps/pull/65) to merge our project into the Django itself.
|
||||||
You show your support by linking the PR.
|
You can show your support by liking the PR.
|
||||||
|
|
||||||
### Is it safe to use this in production?
|
### Is it safe to use this in production?
|
||||||
|
|
||||||
@@ -82,12 +82,24 @@ when you will try to use `QuerySet[MyModel]`, `Manager[MyModel]` or some other D
|
|||||||
|
|
||||||
This happens because these Django classes do not support [`__class_getitem__`](https://www.python.org/dev/peps/pep-0560/#class-getitem) magic method in runtime.
|
This happens because these Django classes do not support [`__class_getitem__`](https://www.python.org/dev/peps/pep-0560/#class-getitem) magic method in runtime.
|
||||||
|
|
||||||
1. You can go with our
|
1. You can go with our [`django_stubs_ext`](https://github.com/typeddjango/django-stubs/tree/master/django_stubs_ext) helper, that patches all the types we use as Generic in django.
|
||||||
|
|
||||||
|
Install it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install django-stubs-ext # as a production dependency
|
||||||
|
```
|
||||||
|
|
||||||
|
And then place in your `manage.py`, `wsgi.py`, and `asgi.py` files:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import django_stubs_ext
|
||||||
|
|
||||||
|
django_stubs_ext.monkeypath()
|
||||||
|
```
|
||||||
|
|
||||||
2. You can use strings instead: `'QuerySet[MyModel]'` and `'Manager[MyModel]'`, this way it will work as a type for `mypy` and as a regular `str` in runtime.
|
2. You can use strings instead: `'QuerySet[MyModel]'` and `'Manager[MyModel]'`, this way it will work as a type for `mypy` and as a regular `str` in runtime.
|
||||||
|
|
||||||
Currently we [are working](https://github.com/django/django/pull/12405) on providing `__class_getitem__` to the classes where we need them.
|
|
||||||
|
|
||||||
### How can I create a HttpRequest that's guaranteed to have an authenticated user?
|
### How can I create a HttpRequest that's guaranteed to have an authenticated user?
|
||||||
|
|
||||||
Django's built in `HttpRequest` has the attribute `user` that resolves to the type
|
Django's built in `HttpRequest` has the attribute `user` that resolves to the type
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
from .monkeypatch import monkeypatch
|
from .patch import monkeypatch as monkeypatch
|
||||||
|
|
||||||
__all__ = ["monkeypatch"]
|
__all__ = ["monkeypatch"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import Any, Generic, List, Optional, Tuple, Type, TypeVar
|
from typing import Any, Generic, List, Optional, Tuple, Type, TypeVar
|
||||||
|
|
||||||
import django
|
from django import VERSION as VERSION
|
||||||
from django.contrib.admin import ModelAdmin
|
from django.contrib.admin import ModelAdmin
|
||||||
from django.contrib.admin.options import BaseModelAdmin
|
from django.contrib.admin.options import BaseModelAdmin
|
||||||
from django.db.models.manager import BaseManager
|
from django.db.models.manager import BaseManager
|
||||||
@@ -21,9 +21,6 @@ class MPGeneric(Generic[_T]):
|
|||||||
possible issues we may run into with this method.
|
possible issues we may run into with this method.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
version: Optional[int]
|
|
||||||
cls: Type[_T]
|
|
||||||
|
|
||||||
def __init__(self, cls: Type[_T], version: Optional[_VersionSpec] = None):
|
def __init__(self, cls: Type[_T], version: Optional[_VersionSpec] = None):
|
||||||
"""Set the data fields, basic constructor."""
|
"""Set the data fields, basic constructor."""
|
||||||
self.version = version
|
self.version = version
|
||||||
@@ -31,7 +28,7 @@ class MPGeneric(Generic[_T]):
|
|||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""Better representation in tests and debug."""
|
"""Better representation in tests and debug."""
|
||||||
return "<MPGeneric: {0}, versions={1}>".format(self.cls, self.version or "all")
|
return "<MPGeneric: {}, versions={}>".format(self.cls, self.version or "all")
|
||||||
|
|
||||||
|
|
||||||
# certain django classes need to be generic, but lack the __class_getitem__ dunder needed to
|
# certain django classes need to be generic, but lack the __class_getitem__ dunder needed to
|
||||||
@@ -51,7 +48,7 @@ _need_generic: List[MPGeneric[Any]] = [
|
|||||||
def monkeypatch() -> None:
|
def monkeypatch() -> None:
|
||||||
"""Monkey patch django as necessary to work properly with mypy."""
|
"""Monkey patch django as necessary to work properly with mypy."""
|
||||||
suited_for_this_version = filter(
|
suited_for_this_version = filter(
|
||||||
spec.version is None or django.VERSION[:2] <= spec.version,
|
lambda spec: spec.version is None or VERSION[:2] <= spec.version,
|
||||||
_need_generic,
|
_need_generic,
|
||||||
)
|
)
|
||||||
for el in suited_for_this_version:
|
for el in suited_for_this_version:
|
||||||
@@ -1,25 +1,43 @@
|
|||||||
|
from contextlib import suppress
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from _pytest.fixtures import FixtureRequest
|
||||||
|
from _pytest.monkeypatch import MonkeyPatch
|
||||||
|
from typing_extensions import Protocol
|
||||||
|
|
||||||
import django_stubs_ext
|
import django_stubs_ext
|
||||||
from django_stubs_ext.monkeypatch import _need_generic, _VersionSpec, django
|
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: Optional[_VersionSpec] = None) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def make_generic_classes(request, monkeypatch):
|
def make_generic_classes(
|
||||||
def fin():
|
request: FixtureRequest,
|
||||||
|
monkeypatch: MonkeyPatch,
|
||||||
|
) -> _MakeGenericClasses:
|
||||||
|
def fin() -> None:
|
||||||
for el in _need_generic:
|
for el in _need_generic:
|
||||||
|
with suppress(AttributeError):
|
||||||
delattr(el.cls, "__class_getitem__")
|
delattr(el.cls, "__class_getitem__")
|
||||||
|
|
||||||
def factory(django_version=None):
|
def factory(django_version: Optional[_VersionSpec] = None) -> None:
|
||||||
if django_version is not None:
|
if django_version is not None:
|
||||||
monkeypatch.setattr(django, "VERSION", django_version)
|
monkeypatch.setattr(patch, "VERSION", django_version)
|
||||||
django_stubs_ext.monkeypatch()
|
django_stubs_ext.monkeypatch()
|
||||||
|
|
||||||
request.addfinalizer(fin)
|
request.addfinalizer(fin)
|
||||||
return factory
|
return factory
|
||||||
|
|
||||||
|
|
||||||
def test_patched_generics(make_generic_classes) -> None:
|
def test_patched_generics(make_generic_classes: _MakeGenericClasses) -> None:
|
||||||
"""Test that the generics actually get patched."""
|
"""Test that the generics actually get patched."""
|
||||||
make_generic_classes()
|
make_generic_classes()
|
||||||
|
|
||||||
@@ -39,11 +57,11 @@ def test_patched_generics(make_generic_classes) -> None:
|
|||||||
)
|
)
|
||||||
def test_patched_version_specific(
|
def test_patched_version_specific(
|
||||||
django_version: _VersionSpec,
|
django_version: _VersionSpec,
|
||||||
make_generic_classes,
|
make_generic_classes: _MakeGenericClasses,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test version speicific types."""
|
"""Test version speicific types."""
|
||||||
make_generic_classes(django_version)
|
make_generic_classes(django_version)
|
||||||
|
|
||||||
for el in _need_generic:
|
for el in _need_generic:
|
||||||
if el.version is not None and el.version[:2] <= django_version:
|
if el.version is not None and django_version <= el.version:
|
||||||
assert el.cls[int] is el.cls
|
assert el.cls[int] is el.cls
|
||||||
|
|||||||
Reference in New Issue
Block a user