move configuration to [mypy.plugins.django-stubs] inside mypy config file

This commit is contained in:
Maxim Kurnikov
2019-07-24 22:32:52 +03:00
parent e6d7a570e8
commit b6a5ccabdf
9 changed files with 63 additions and 61 deletions

View File

@@ -10,6 +10,7 @@ from django.db.models.fields.reverse_related import ForeignObjectRel
from django.db.models.sql.query import Query from django.db.models.sql.query import Query
from django.utils.functional import cached_property from django.utils.functional import cached_property
from mypy.checker import TypeChecker from mypy.checker import TypeChecker
from mypy.errors import Errors
from mypy.types import Instance, Type as MypyType from mypy.types import Instance, Type as MypyType
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
@@ -139,13 +140,11 @@ class DjangoLookupsContext:
class DjangoContext: class DjangoContext:
def __init__(self, plugin_toml_config: Optional[Dict[str, Any]]) -> None: def __init__(self, django_settings_module: str) -> None:
self.fields_context = DjangoFieldsContext(self) self.fields_context = DjangoFieldsContext(self)
self.lookups_context = DjangoLookupsContext(self) self.lookups_context = DjangoLookupsContext(self)
self.django_settings_module = None self.django_settings_module = django_settings_module
if plugin_toml_config:
self.django_settings_module = plugin_toml_config.get('django_settings_module', None)
self.apps_registry: Optional[Dict[str, str]] = None self.apps_registry: Optional[Dict[str, str]] = None
self.settings: LazySettings = None self.settings: LazySettings = None

View File

@@ -1,9 +1,9 @@
import os import configparser
from functools import partial from functools import partial
from typing import Callable, Dict, List, Optional, Tuple from typing import Callable, Dict, List, Optional, Tuple
import toml
from django.db.models.fields.related import RelatedField from django.db.models.fields.related import RelatedField
from mypy.errors import Errors
from mypy.nodes import MypyFile, TypeInfo from mypy.nodes import MypyFile, TypeInfo
from mypy.options import Options from mypy.options import Options
from mypy.plugin import ( from mypy.plugin import (
@@ -47,17 +47,33 @@ def add_new_manager_base(ctx: ClassDefContext) -> None:
helpers.get_django_metadata(sym.node)['manager_bases'][ctx.cls.fullname] = 1 helpers.get_django_metadata(sym.node)['manager_bases'][ctx.cls.fullname] = 1
def extract_django_settings_module(config_file_path: Optional[str]) -> str:
errors = Errors()
if config_file_path is None:
errors.report(0, None, "'django_settings_module' is not set: no mypy config file specified")
errors.raise_error()
parser = configparser.ConfigParser()
parser.read(config_file_path)
if not parser.has_section('mypy.plugins.django-stubs'):
errors.report(0, None, "'django_settings_module' is not set: no section [mypy.plugins.django-stubs]",
file=config_file_path)
errors.raise_error()
if not parser.has_option('mypy.plugins.django-stubs', 'django_settings_module'):
errors.report(0, None, "'django_settings_module' is not set: setting is not provided",
file=config_file_path)
errors.raise_error()
django_settings_module = parser.get('mypy.plugins.django-stubs', 'django_settings_module').strip('\'"')
return django_settings_module
class NewSemanalDjangoPlugin(Plugin): class NewSemanalDjangoPlugin(Plugin):
def __init__(self, options: Options) -> None: def __init__(self, options: Options) -> None:
super().__init__(options) super().__init__(options)
django_settings_module = extract_django_settings_module(options.config_file)
plugin_toml_config = None self.django_context = DjangoContext(django_settings_module)
if os.path.exists('pyproject.toml'):
with open('pyproject.toml', 'r') as f:
pyproject_toml = toml.load(f)
plugin_toml_config = pyproject_toml.get('tool', {}).get('django-stubs')
self.django_context = DjangoContext(plugin_toml_config)
def _get_current_queryset_bases(self) -> Dict[str, int]: def _get_current_queryset_bases(self) -> Dict[str, int]:
model_sym = self.lookup_fully_qualified(fullnames.QUERYSET_CLASS_FULLNAME) model_sym = self.lookup_fully_qualified(fullnames.QUERYSET_CLASS_FULLNAME)

View File

@@ -3,26 +3,24 @@ from pytest_mypy.item import YamlTestItem
def django_plugin_hook(test_item: YamlTestItem) -> None: def django_plugin_hook(test_item: YamlTestItem) -> None:
settings = { custom_settings = test_item.parsed_test_data.get('custom_settings', '')
'SECRET_KEY': '"1"',
}
additional_settings = test_item.parsed_test_data.get('additional_settings')
if additional_settings:
for item in additional_settings:
name, _, val = item.partition('=')
settings[name] = val
installed_apps = test_item.parsed_test_data.get('installed_apps', None) installed_apps = test_item.parsed_test_data.get('installed_apps', None)
if installed_apps and custom_settings:
raise ValueError('"installed_apps" and "custom_settings" are not compatible, please use one or the other')
if installed_apps is not None: if installed_apps is not None:
# custom_settings is empty, add INSTALLED_APPS
installed_apps += ['django.contrib.contenttypes'] installed_apps += ['django.contrib.contenttypes']
installed_apps_as_str = '(' + ','.join([repr(app) for app in installed_apps]) + ',)' installed_apps_as_str = '(' + ','.join([repr(app) for app in installed_apps]) + ',)'
custom_settings += 'INSTALLED_APPS = ' + installed_apps_as_str
pyproject_toml_file = File(path='pyproject.toml', if 'SECRET_KEY' not in custom_settings:
content='[tool.django-stubs]\ndjango_settings_module=\'mysettings\'') custom_settings = 'SECRET_KEY = "1"\n' + custom_settings
test_item.files.append(pyproject_toml_file)
settings_contents = f'INSTALLED_APPS={installed_apps_as_str}\n' if not test_item.additional_mypy_config:
settings_contents += '\n'.join([f'{key}={val}' for key, val in settings.items()]) test_item.additional_mypy_config = "[mypy.plugins.django-stubs]\n" \
"django_settings_module = mysettings"
mysettings_file = File(path='mysettings.py', content=settings_contents) mysettings_file = File(path='mysettings.py', content=custom_settings)
test_item.files.append(mysettings_file) test_item.files.append(mysettings_file)

View File

@@ -22,7 +22,6 @@ with open('README.md', 'r') as f:
dependencies = [ dependencies = [
'mypy>=0.720,<0.730', 'mypy>=0.720,<0.730',
'typing-extensions', 'typing-extensions',
'toml',
'django' 'django'
] ]

View File

@@ -435,10 +435,9 @@
from myapp.models import Book from myapp.models import Book
book = Book() book = Book()
reveal_type(book.publisher) # N: Revealed type is 'myapp.models.Publisher*' reveal_type(book.publisher) # N: Revealed type is 'myapp.models.Publisher*'
installed_apps: custom_settings: |
- myapp INSTALLED_APPS = ('django.contrib.contenttypes', 'myapp')
additional_settings: BOOK_RELATED_MODEL = 'myapp.Publisher'
- BOOK_RELATED_MODEL='myapp.Publisher'
files: files:
- path: myapp/__init__.py - path: myapp/__init__.py
- path: myapp/models.py - path: myapp/models.py

View File

@@ -5,15 +5,13 @@
reveal_type(mymodel.id) # N: Revealed type is 'builtins.int*' reveal_type(mymodel.id) # N: Revealed type is 'builtins.int*'
reveal_type(mymodel.user) # N: Revealed type is 'django.contrib.auth.models.User*' reveal_type(mymodel.user) # N: Revealed type is 'django.contrib.auth.models.User*'
reveal_type(mymodel.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.MyModel]' reveal_type(mymodel.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.MyModel]'
mypy_config: |
[mypy.plugins.django-stubs]
django_settings_module = mysettings
custom_settings: |
SECRET_KEY = '1'
INSTALLED_APPS = ('django.contrib.contenttypes', 'django.contrib.auth', 'myapp')
files: files:
- path: pyproject.toml
content: |
[tool.django-stubs]
django_settings_module = 'mysettings'
- path: mysettings.py
content: |
SECRET_KEY = '1'
INSTALLED_APPS = ('django.contrib.contenttypes', 'django.contrib.auth', 'myapp')
- path: myapp/__init__.py - path: myapp/__init__.py
- path: myapp/models.py - path: myapp/models.py
content: | content: |

View File

@@ -5,10 +5,9 @@
reveal_type(HttpRequest().user) # N: Revealed type is 'myapp.models.MyUser' reveal_type(HttpRequest().user) # N: Revealed type is 'myapp.models.MyUser'
# check that other fields work ok # check that other fields work ok
reveal_type(HttpRequest().method) # N: Revealed type is 'Union[builtins.str, None]' reveal_type(HttpRequest().method) # N: Revealed type is 'Union[builtins.str, None]'
installed_apps: custom_settings: |
- myapp INSTALLED_APPS = ('django.contrib.contenttypes', 'myapp')
additional_settings: AUTH_USER_MODEL='myapp.MyUser'
- AUTH_USER_MODEL='myapp.MyUser'
files: files:
- path: myapp/__init__.py - path: myapp/__init__.py
- path: myapp/models.py - path: myapp/models.py

View File

@@ -8,17 +8,12 @@
reveal_type(settings.APPS_DIR) # N: Revealed type is 'pathlib.Path' reveal_type(settings.APPS_DIR) # N: Revealed type is 'pathlib.Path'
reveal_type(settings.NUMBERS) # N: Revealed type is 'builtins.list[builtins.str*]' reveal_type(settings.NUMBERS) # N: Revealed type is 'builtins.list[builtins.str*]'
reveal_type(settings.DICT) # N: Revealed type is 'builtins.dict[Any, Any]' reveal_type(settings.DICT) # N: Revealed type is 'builtins.dict[Any, Any]'
custom_settings: |
from base import *
SECRET_KEY = 112233
NUMBERS = ['one', 'two']
DICT = {} # type: ignore
files: files:
- path: pyproject.toml
content: |
[tool.django-stubs]
django_settings_module = 'mysettings'
- path: mysettings.py
content: |
from base import *
SECRET_KEY = 112233
NUMBERS = ['one', 'two']
DICT = {} # type: ignore
- path: base.py - path: base.py
content: | content: |
from pathlib import Path from pathlib import Path

View File

@@ -26,10 +26,9 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
UserModel = get_user_model() UserModel = get_user_model()
reveal_type(UserModel.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.MyUser]' reveal_type(UserModel.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.MyUser]'
additional_settings: custom_settings: |
- AUTH_USER_MODEL='myapp.MyUser' INSTALLED_APPS = ('django.contrib.contenttypes', 'myapp')
installed_apps: AUTH_USER_MODEL = 'myapp.MyUser'
- myapp
files: files:
- path: myapp/__init__.py - path: myapp/__init__.py
- path: myapp/models.py - path: myapp/models.py