mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-06 12:14:28 +08:00
add support for django.conf.settings.SETTING_NAME
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
from typing import Dict, Optional, Type, Tuple, NamedTuple
|
||||
from typing import Dict, Optional, NamedTuple
|
||||
|
||||
from mypy.nodes import SymbolTableNode, Var, Expression, MemberExpr
|
||||
from mypy.semanal import SemanticAnalyzerPass2
|
||||
from mypy.types import Type
|
||||
from mypy.nodes import SymbolTableNode, Var, Expression
|
||||
from mypy.plugin import FunctionContext
|
||||
from mypy.types import Instance
|
||||
from mypy.types import Instance, UnionType, NoneTyp
|
||||
|
||||
MODEL_CLASS_FULLNAME = 'django.db.models.base.Model'
|
||||
QUERYSET_CLASS_FULLNAME = 'django.db.models.query.QuerySet'
|
||||
@@ -13,7 +15,6 @@ ONETOONE_FIELD_FULLNAME = 'django.db.models.fields.related.OneToOneField'
|
||||
def create_new_symtable_node(name: str, kind: int, instance: Instance) -> SymbolTableNode:
|
||||
new_var = Var(name, instance)
|
||||
new_var.info = instance.type
|
||||
|
||||
return SymbolTableNode(kind, new_var,
|
||||
plugin_generated=True)
|
||||
|
||||
@@ -53,3 +54,14 @@ def get_call_signature_or_none(ctx: FunctionContext) -> Optional[Dict[str, Argum
|
||||
result[arg_name] = (arg[0], arg_type[0])
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def make_optional(typ: Type) -> Type:
|
||||
return UnionType.make_simplified_union([typ, NoneTyp()])
|
||||
|
||||
|
||||
def make_required(typ: Type) -> Type:
|
||||
if not isinstance(typ, UnionType):
|
||||
return typ
|
||||
items = [item for item in typ.items if not isinstance(item, NoneTyp)]
|
||||
return UnionType.make_union(items)
|
||||
@@ -1,5 +1,8 @@
|
||||
import os
|
||||
from typing import Callable, Optional
|
||||
|
||||
from django.conf import Settings
|
||||
from mypy.options import Options
|
||||
from mypy.plugin import Plugin, FunctionContext, ClassDefContext
|
||||
from mypy.types import Type
|
||||
|
||||
@@ -8,6 +11,7 @@ from mypy_django_plugin.plugins.objects_queryset import set_objects_queryset_to_
|
||||
from mypy_django_plugin.plugins.postgres_fields import determine_type_of_array_field
|
||||
from mypy_django_plugin.plugins.related_fields import set_related_name_instance_for_onetoonefield, \
|
||||
set_related_name_manager_for_foreign_key, set_fieldname_attrs_for_related_fields
|
||||
from mypy_django_plugin.plugins.setup_settings import DjangoConfSettingsInitializerHook
|
||||
|
||||
|
||||
base_model_classes = {helpers.MODEL_CLASS_FULLNAME}
|
||||
@@ -21,6 +25,15 @@ def transform_model_class(ctx: ClassDefContext) -> None:
|
||||
|
||||
|
||||
class DjangoPlugin(Plugin):
|
||||
def __init__(self,
|
||||
options: Options) -> None:
|
||||
super().__init__(options)
|
||||
self.django_settings = None
|
||||
|
||||
django_settings_module = os.environ.get('DJANGO_SETTINGS_MODULE')
|
||||
if django_settings_module:
|
||||
self.django_settings = Settings(django_settings_module)
|
||||
|
||||
def get_function_hook(self, fullname: str
|
||||
) -> Optional[Callable[[FunctionContext], Type]]:
|
||||
if fullname == helpers.FOREIGN_KEY_FULLNAME:
|
||||
@@ -37,6 +50,8 @@ class DjangoPlugin(Plugin):
|
||||
) -> Optional[Callable[[ClassDefContext], None]]:
|
||||
if fullname in base_model_classes:
|
||||
return transform_model_class
|
||||
if fullname == 'django.conf._DjangoConfLazyObject':
|
||||
return DjangoConfSettingsInitializerHook(settings=self.django_settings)
|
||||
return None
|
||||
|
||||
|
||||
34
mypy_django_plugin/plugins/setup_settings.py
Normal file
34
mypy_django_plugin/plugins/setup_settings.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from typing import cast, Any
|
||||
|
||||
from django.conf import Settings
|
||||
from mypy.nodes import MDEF, TypeInfo, SymbolTable
|
||||
from mypy.plugin import ClassDefContext
|
||||
from mypy.semanal import SemanticAnalyzerPass2
|
||||
from mypy.types import Instance, AnyType, TypeOfAny
|
||||
|
||||
from mypy_django_plugin import helpers
|
||||
|
||||
|
||||
def get_obj_type_name(value: Any) -> str:
|
||||
return type(value).__module__ + '.' + type(value).__qualname__
|
||||
|
||||
|
||||
class DjangoConfSettingsInitializerHook(object):
|
||||
def __init__(self, settings: Settings):
|
||||
self.settings = settings
|
||||
|
||||
def __call__(self, ctx: ClassDefContext) -> None:
|
||||
api = cast(SemanticAnalyzerPass2, ctx.api)
|
||||
for name, value in self.settings.__dict__.items():
|
||||
if name.isupper():
|
||||
if value is None:
|
||||
ctx.cls.info.names[name] = helpers.create_new_symtable_node(name, MDEF,
|
||||
instance=api.builtin_type('builtins.object'))
|
||||
continue
|
||||
|
||||
type_fullname = get_obj_type_name(value)
|
||||
sym = api.lookup_fully_qualified_or_none(type_fullname)
|
||||
if sym is not None:
|
||||
args = len(sym.node.type_vars) * [AnyType(TypeOfAny.from_omitted_generics)]
|
||||
ctx.cls.info.names[name] = helpers.create_new_symtable_node(name, MDEF,
|
||||
instance=Instance(sym.node, args))
|
||||
Reference in New Issue
Block a user