mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-07 12:44:29 +08:00
add config file support
This commit is contained in:
17
README.md
17
README.md
@@ -24,12 +24,23 @@ plugins =
|
|||||||
in your `mypy.ini` file.
|
in your `mypy.ini` file.
|
||||||
|
|
||||||
|
|
||||||
### `django.conf.settings` support
|
## Configuration
|
||||||
|
|
||||||
`settings.SETTING_NAME` will only work if `DJANGO_SETTINGS_MODULE` will be present in the environment, when mypy is executed.
|
In order to specify config file, set `MYPY_DJANGO_CONFIG` environment variable with path to the config file.
|
||||||
|
|
||||||
If some setting is not recognized to the plugin, but it's clearly there, try adding type annotation to it.
|
Config file format (.ini):
|
||||||
|
```
|
||||||
|
[mypy_django_plugin]
|
||||||
|
|
||||||
|
# specify settings module to use for django.conf.settings, this setting
|
||||||
|
# could also be specified with DJANGO_SETTINGS_MODULE environment variable
|
||||||
|
# (it also takes priority over config file)
|
||||||
|
django_settings = mysettings.local
|
||||||
|
|
||||||
|
# if True, all unknown settings in django.conf.settings will fallback to Any,
|
||||||
|
# specify it if your settings are loaded dynamically to avoid false positives
|
||||||
|
ignore_missing_settings = True
|
||||||
|
```
|
||||||
|
|
||||||
## To get help
|
## To get help
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
|
from configparser import ConfigParser
|
||||||
from typing import Callable, Dict, Optional, cast
|
from typing import Callable, Dict, Optional, cast
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
from mypy.checker import TypeChecker
|
from mypy.checker import TypeChecker
|
||||||
from mypy.nodes import TypeInfo
|
from mypy.nodes import TypeInfo
|
||||||
from mypy.options import Options
|
from mypy.options import Options
|
||||||
@@ -89,6 +91,23 @@ def set_primary_key_marking(ctx: FunctionContext) -> Type:
|
|||||||
return ctx.default_return_type
|
return ctx.default_return_type
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Config:
|
||||||
|
django_settings_module: Optional[str] = None
|
||||||
|
ignore_missing_settings: bool = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_config_file(self, fpath: str) -> 'Config':
|
||||||
|
ini_config = ConfigParser()
|
||||||
|
ini_config.read(fpath)
|
||||||
|
if not ini_config.has_section('mypy_django_plugin'):
|
||||||
|
raise ValueError('Invalid config file: no [mypy_django_plugin] section')
|
||||||
|
return Config(django_settings_module=ini_config.get('mypy_django_plugin', 'django_settings',
|
||||||
|
fallback=None),
|
||||||
|
ignore_missing_settings=ini_config.get('mypy_django_plugin', 'ignore_missing_settings',
|
||||||
|
fallback=False))
|
||||||
|
|
||||||
|
|
||||||
class DjangoPlugin(Plugin):
|
class DjangoPlugin(Plugin):
|
||||||
def __init__(self, options: Options) -> None:
|
def __init__(self, options: Options) -> None:
|
||||||
super().__init__(options)
|
super().__init__(options)
|
||||||
@@ -96,8 +115,18 @@ class DjangoPlugin(Plugin):
|
|||||||
monkeypatch.restore_original_load_graph()
|
monkeypatch.restore_original_load_graph()
|
||||||
monkeypatch.restore_original_dependencies_handling()
|
monkeypatch.restore_original_dependencies_handling()
|
||||||
|
|
||||||
|
config_fpath = os.environ.get('MYPY_DJANGO_CONFIG')
|
||||||
|
if config_fpath:
|
||||||
|
self.config = Config.from_config_file(config_fpath)
|
||||||
|
self.django_settings = self.config.django_settings_module
|
||||||
|
else:
|
||||||
|
self.config = Config()
|
||||||
|
self.django_settings = None
|
||||||
|
|
||||||
|
if 'DJANGO_SETTINGS_MODULE' in os.environ:
|
||||||
|
self.django_settings = os.environ['DJANGO_SETTINGS_MODULE']
|
||||||
|
|
||||||
settings_modules = ['django.conf.global_settings']
|
settings_modules = ['django.conf.global_settings']
|
||||||
self.django_settings = os.environ.get('DJANGO_SETTINGS_MODULE')
|
|
||||||
if self.django_settings:
|
if self.django_settings:
|
||||||
settings_modules.append(self.django_settings)
|
settings_modules.append(self.django_settings)
|
||||||
|
|
||||||
@@ -163,7 +192,8 @@ class DjangoPlugin(Plugin):
|
|||||||
settings_modules = ['django.conf.global_settings']
|
settings_modules = ['django.conf.global_settings']
|
||||||
if self.django_settings:
|
if self.django_settings:
|
||||||
settings_modules.append(self.django_settings)
|
settings_modules.append(self.django_settings)
|
||||||
return AddSettingValuesToDjangoConfObject(settings_modules)
|
return AddSettingValuesToDjangoConfObject(settings_modules,
|
||||||
|
self.config.ignore_missing_settings)
|
||||||
|
|
||||||
if fullname in self._get_current_manager_bases():
|
if fullname in self._get_current_manager_bases():
|
||||||
return transform_manager_class
|
return transform_manager_class
|
||||||
|
|||||||
@@ -47,11 +47,15 @@ def load_settings_from_module(settings_classdef: ClassDef, module: MypyFile) ->
|
|||||||
|
|
||||||
|
|
||||||
class AddSettingValuesToDjangoConfObject:
|
class AddSettingValuesToDjangoConfObject:
|
||||||
def __init__(self, settings_modules: List[str]):
|
def __init__(self, settings_modules: List[str], ignore_missing_settings: bool):
|
||||||
self.settings_modules = settings_modules
|
self.settings_modules = settings_modules
|
||||||
|
self.ignore_missing_settings = ignore_missing_settings
|
||||||
|
|
||||||
def __call__(self, ctx: ClassDefContext) -> None:
|
def __call__(self, ctx: ClassDefContext) -> None:
|
||||||
api = cast(SemanticAnalyzerPass2, ctx.api)
|
api = cast(SemanticAnalyzerPass2, ctx.api)
|
||||||
for module_name in self.settings_modules:
|
for module_name in self.settings_modules:
|
||||||
module = api.modules[module_name]
|
module = api.modules[module_name]
|
||||||
load_settings_from_module(ctx.cls, module=module)
|
load_settings_from_module(ctx.cls, module=module)
|
||||||
|
|
||||||
|
if self.ignore_missing_settings:
|
||||||
|
ctx.cls.info.fallback_to_any = True
|
||||||
|
|||||||
23
test-data/typecheck/config.test
Normal file
23
test-data/typecheck/config.test
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
[CASE missing_settings_ignored_flag]
|
||||||
|
from django.conf import settings
|
||||||
|
reveal_type(settings.NO_SUCH_SETTING) # E: Revealed type is 'Any'
|
||||||
|
|
||||||
|
[env MYPY_DJANGO_CONFIG=${MYPY_CWD}/mypy_django.ini]
|
||||||
|
|
||||||
|
[file mypy_django.ini]
|
||||||
|
[[mypy_django_plugin]
|
||||||
|
ignore_missing_settings = True
|
||||||
|
[out]
|
||||||
|
|
||||||
|
[CASE django_settings_via_config_file]
|
||||||
|
from django.conf import settings
|
||||||
|
reveal_type(settings.MY_SETTING) # E: Revealed type is 'builtins.int'
|
||||||
|
|
||||||
|
[env MYPY_DJANGO_CONFIG=${MYPY_CWD}/mypy_django.ini]
|
||||||
|
[file mypy_django.ini]
|
||||||
|
[[mypy_django_plugin]
|
||||||
|
django_settings = mysettings
|
||||||
|
|
||||||
|
[file mysettings.py]
|
||||||
|
MY_SETTING: int = 1
|
||||||
|
[out]
|
||||||
Reference in New Issue
Block a user