mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-07 04:34:29 +08:00
rework settings, add loading of the django.conf.global_settings, cleanups
This commit is contained in:
@@ -23,7 +23,13 @@ plugins =
|
||||
|
||||
in your `mypy.ini` file.
|
||||
|
||||
Also, it uses value of `DJANGO_SETTINGS_MODULE` from the environment, so set it before execution, otherwise some features will not work.
|
||||
|
||||
### `django.conf.settings` support
|
||||
|
||||
`settings.SETTING_NAME` will only work if `DJANGO_SETTINGS_MODULE` will be present in the environment, when mypy is executed.
|
||||
|
||||
If some setting is not recognized to the plugin, but it's clearly there, try adding type annotation to it.
|
||||
|
||||
|
||||
## To get help
|
||||
|
||||
|
||||
@@ -5,156 +5,67 @@ by the DJANGO_SETTINGS_MODULE environment variable.
|
||||
|
||||
# This is defined here as a do-nothing function because we can't import
|
||||
# django.utils.translation -- that module depends on the settings.
|
||||
def gettext_noop(s):
|
||||
return s
|
||||
from typing import Any, Dict, List, Optional, Pattern, Tuple, Protocol, Union, Callable, TYPE_CHECKING
|
||||
|
||||
####################
|
||||
# CORE #
|
||||
####################
|
||||
if TYPE_CHECKING:
|
||||
from django.db.models.base import Model
|
||||
|
||||
DEBUG = False
|
||||
DEBUG: bool = ...
|
||||
|
||||
# Whether the framework should propagate raw exceptions rather than catching
|
||||
# them. This is useful under some testing situations and should never be used
|
||||
# on a live site.
|
||||
DEBUG_PROPAGATE_EXCEPTIONS = False
|
||||
DEBUG_PROPAGATE_EXCEPTIONS: bool = ...
|
||||
|
||||
# People who get code error notifications.
|
||||
# In the format [('Full Name', 'email@example.com'), ('Full Name', 'anotheremail@example.com')]
|
||||
ADMINS = []
|
||||
ADMINS: List[Tuple[str, str]] = ...
|
||||
|
||||
# List of IP addresses, as strings, that:
|
||||
# * See debug comments, when DEBUG is true
|
||||
# * Receive x-headers
|
||||
INTERNAL_IPS = []
|
||||
INTERNAL_IPS: List[str] = ...
|
||||
|
||||
# Hosts/domain names that are valid for this site.
|
||||
# "*" matches anything, ".example.com" matches example.com and all subdomains
|
||||
ALLOWED_HOSTS = []
|
||||
ALLOWED_HOSTS: List[str] = ...
|
||||
|
||||
# Local time zone for this installation. All choices can be found here:
|
||||
# https://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
|
||||
# systems may support all possibilities). When USE_TZ is True, this is
|
||||
# interpreted as the default user time zone.
|
||||
TIME_ZONE = "America/Chicago"
|
||||
TIME_ZONE: str = ...
|
||||
|
||||
# If you set this to True, Django will use timezone-aware datetimes.
|
||||
USE_TZ = False
|
||||
USE_TZ: bool = ...
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||
LANGUAGE_CODE = "en-us"
|
||||
LANGUAGE_CODE: str = ...
|
||||
|
||||
# Languages we provide translations for, out of the box.
|
||||
LANGUAGES = [
|
||||
("af", gettext_noop("Afrikaans")),
|
||||
("ar", gettext_noop("Arabic")),
|
||||
("ast", gettext_noop("Asturian")),
|
||||
("az", gettext_noop("Azerbaijani")),
|
||||
("bg", gettext_noop("Bulgarian")),
|
||||
("be", gettext_noop("Belarusian")),
|
||||
("bn", gettext_noop("Bengali")),
|
||||
("br", gettext_noop("Breton")),
|
||||
("bs", gettext_noop("Bosnian")),
|
||||
("ca", gettext_noop("Catalan")),
|
||||
("cs", gettext_noop("Czech")),
|
||||
("cy", gettext_noop("Welsh")),
|
||||
("da", gettext_noop("Danish")),
|
||||
("de", gettext_noop("German")),
|
||||
("dsb", gettext_noop("Lower Sorbian")),
|
||||
("el", gettext_noop("Greek")),
|
||||
("en", gettext_noop("English")),
|
||||
("en-au", gettext_noop("Australian English")),
|
||||
("en-gb", gettext_noop("British English")),
|
||||
("eo", gettext_noop("Esperanto")),
|
||||
("es", gettext_noop("Spanish")),
|
||||
("es-ar", gettext_noop("Argentinian Spanish")),
|
||||
("es-co", gettext_noop("Colombian Spanish")),
|
||||
("es-mx", gettext_noop("Mexican Spanish")),
|
||||
("es-ni", gettext_noop("Nicaraguan Spanish")),
|
||||
("es-ve", gettext_noop("Venezuelan Spanish")),
|
||||
("et", gettext_noop("Estonian")),
|
||||
("eu", gettext_noop("Basque")),
|
||||
("fa", gettext_noop("Persian")),
|
||||
("fi", gettext_noop("Finnish")),
|
||||
("fr", gettext_noop("French")),
|
||||
("fy", gettext_noop("Frisian")),
|
||||
("ga", gettext_noop("Irish")),
|
||||
("gd", gettext_noop("Scottish Gaelic")),
|
||||
("gl", gettext_noop("Galician")),
|
||||
("he", gettext_noop("Hebrew")),
|
||||
("hi", gettext_noop("Hindi")),
|
||||
("hr", gettext_noop("Croatian")),
|
||||
("hsb", gettext_noop("Upper Sorbian")),
|
||||
("hu", gettext_noop("Hungarian")),
|
||||
("ia", gettext_noop("Interlingua")),
|
||||
("id", gettext_noop("Indonesian")),
|
||||
("io", gettext_noop("Ido")),
|
||||
("is", gettext_noop("Icelandic")),
|
||||
("it", gettext_noop("Italian")),
|
||||
("ja", gettext_noop("Japanese")),
|
||||
("ka", gettext_noop("Georgian")),
|
||||
("kab", gettext_noop("Kabyle")),
|
||||
("kk", gettext_noop("Kazakh")),
|
||||
("km", gettext_noop("Khmer")),
|
||||
("kn", gettext_noop("Kannada")),
|
||||
("ko", gettext_noop("Korean")),
|
||||
("lb", gettext_noop("Luxembourgish")),
|
||||
("lt", gettext_noop("Lithuanian")),
|
||||
("lv", gettext_noop("Latvian")),
|
||||
("mk", gettext_noop("Macedonian")),
|
||||
("ml", gettext_noop("Malayalam")),
|
||||
("mn", gettext_noop("Mongolian")),
|
||||
("mr", gettext_noop("Marathi")),
|
||||
("my", gettext_noop("Burmese")),
|
||||
("nb", gettext_noop("Norwegian Bokmål")),
|
||||
("ne", gettext_noop("Nepali")),
|
||||
("nl", gettext_noop("Dutch")),
|
||||
("nn", gettext_noop("Norwegian Nynorsk")),
|
||||
("os", gettext_noop("Ossetic")),
|
||||
("pa", gettext_noop("Punjabi")),
|
||||
("pl", gettext_noop("Polish")),
|
||||
("pt", gettext_noop("Portuguese")),
|
||||
("pt-br", gettext_noop("Brazilian Portuguese")),
|
||||
("ro", gettext_noop("Romanian")),
|
||||
("ru", gettext_noop("Russian")),
|
||||
("sk", gettext_noop("Slovak")),
|
||||
("sl", gettext_noop("Slovenian")),
|
||||
("sq", gettext_noop("Albanian")),
|
||||
("sr", gettext_noop("Serbian")),
|
||||
("sr-latn", gettext_noop("Serbian Latin")),
|
||||
("sv", gettext_noop("Swedish")),
|
||||
("sw", gettext_noop("Swahili")),
|
||||
("ta", gettext_noop("Tamil")),
|
||||
("te", gettext_noop("Telugu")),
|
||||
("th", gettext_noop("Thai")),
|
||||
("tr", gettext_noop("Turkish")),
|
||||
("tt", gettext_noop("Tatar")),
|
||||
("udm", gettext_noop("Udmurt")),
|
||||
("uk", gettext_noop("Ukrainian")),
|
||||
("ur", gettext_noop("Urdu")),
|
||||
("vi", gettext_noop("Vietnamese")),
|
||||
("zh-hans", gettext_noop("Simplified Chinese")),
|
||||
("zh-hant", gettext_noop("Traditional Chinese")),
|
||||
]
|
||||
LANGUAGES: List[Tuple[str, str]] = ...
|
||||
|
||||
# Languages using BiDi (right-to-left) layout
|
||||
LANGUAGES_BIDI = ["he", "ar", "fa", "ur"]
|
||||
LANGUAGES_BIDI: List[str] = ...
|
||||
|
||||
# If you set this to False, Django will make some optimizations so as not
|
||||
# to load the internationalization machinery.
|
||||
USE_I18N = True
|
||||
LOCALE_PATHS = []
|
||||
USE_I18N: bool = ...
|
||||
LOCALE_PATHS: List[str] = ...
|
||||
|
||||
# Settings for language cookie
|
||||
LANGUAGE_COOKIE_NAME = "django_language"
|
||||
LANGUAGE_COOKIE_AGE = None
|
||||
LANGUAGE_COOKIE_DOMAIN = None
|
||||
LANGUAGE_COOKIE_PATH = "/"
|
||||
LANGUAGE_COOKIE_NAME: str = ...
|
||||
LANGUAGE_COOKIE_AGE: Optional[int] = ...
|
||||
LANGUAGE_COOKIE_DOMAIN: Optional[str] = ...
|
||||
LANGUAGE_COOKIE_PATH: str = ...
|
||||
|
||||
# If you set this to True, Django will format dates, numbers and calendars
|
||||
# according to user current locale.
|
||||
USE_L10N = False
|
||||
USE_L10N: bool = ...
|
||||
|
||||
# Not-necessarily-technical managers of the site. They get broken link
|
||||
# notifications and other various emails.
|
||||
@@ -163,66 +74,69 @@ MANAGERS = ADMINS
|
||||
# Default content type and charset to use for all HttpResponse objects, if a
|
||||
# MIME type isn't manually specified. These are used to construct the
|
||||
# Content-Type header.
|
||||
DEFAULT_CONTENT_TYPE = "text/html"
|
||||
DEFAULT_CHARSET = "utf-8"
|
||||
DEFAULT_CONTENT_TYPE: str = ...
|
||||
DEFAULT_CHARSET: str = ...
|
||||
|
||||
# Encoding of files read from disk (template and initial SQL files).
|
||||
FILE_CHARSET = "utf-8"
|
||||
FILE_CHARSET: str = ...
|
||||
|
||||
# Email address that error messages come from.
|
||||
SERVER_EMAIL = "root@localhost"
|
||||
SERVER_EMAIL: str = ...
|
||||
|
||||
# Database connection info. If left empty, will default to the dummy backend.
|
||||
DATABASES = {}
|
||||
DATABASES: Dict[str, Dict[str, Any]] = ...
|
||||
|
||||
# Classes used to implement DB routing behavior.
|
||||
DATABASE_ROUTERS = []
|
||||
class Router(Protocol):
|
||||
def allow_migrate(self, db, app_label, **hints): ...
|
||||
|
||||
DATABASE_ROUTERS: List[Union[str, Router]] = ...
|
||||
|
||||
# The email backend to use. For possible shortcuts see django.core.mail.
|
||||
# The default is to use the SMTP backend.
|
||||
# Third-party backends can be specified by providing a Python path
|
||||
# to a module that defines an EmailBackend class.
|
||||
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
|
||||
EMAIL_BACKEND: str = ...
|
||||
|
||||
# Host for sending email.
|
||||
EMAIL_HOST = "localhost"
|
||||
EMAIL_HOST: str = ...
|
||||
|
||||
# Port for sending email.
|
||||
EMAIL_PORT = 25
|
||||
EMAIL_PORT: int = ...
|
||||
|
||||
# Whether to send SMTP 'Date' header in the local time zone or in UTC.
|
||||
EMAIL_USE_LOCALTIME = False
|
||||
EMAIL_USE_LOCALTIME: bool = ...
|
||||
|
||||
# Optional SMTP authentication information for EMAIL_HOST.
|
||||
EMAIL_HOST_USER = ""
|
||||
EMAIL_HOST_PASSWORD = ""
|
||||
EMAIL_USE_TLS = False
|
||||
EMAIL_USE_SSL = False
|
||||
EMAIL_SSL_CERTFILE = None
|
||||
EMAIL_SSL_KEYFILE = None
|
||||
EMAIL_TIMEOUT = None
|
||||
EMAIL_HOST_USER: str = ...
|
||||
EMAIL_HOST_PASSWORD: str = ...
|
||||
EMAIL_USE_TLS: bool = ...
|
||||
EMAIL_USE_SSL: bool = ...
|
||||
EMAIL_SSL_CERTFILE: Optional[str] = ...
|
||||
EMAIL_SSL_KEYFILE: Optional[str] = ...
|
||||
EMAIL_TIMEOUT: Optional[int] = ...
|
||||
|
||||
# List of strings representing installed apps.
|
||||
INSTALLED_APPS = []
|
||||
INSTALLED_APPS: List[str] = ...
|
||||
|
||||
TEMPLATES = []
|
||||
TEMPLATES: List[Dict[str, Any]] = ...
|
||||
|
||||
# Default form rendering class.
|
||||
FORM_RENDERER = "django.forms.renderers.DjangoTemplates"
|
||||
FORM_RENDERER: str = ...
|
||||
|
||||
# Default email address to use for various automated correspondence from
|
||||
# the site managers.
|
||||
DEFAULT_FROM_EMAIL = "webmaster@localhost"
|
||||
DEFAULT_FROM_EMAIL: str = ...
|
||||
|
||||
# Subject-line prefix for email messages send with django.core.mail.mail_admins
|
||||
# or ...mail_managers. Make sure to include the trailing space.
|
||||
EMAIL_SUBJECT_PREFIX = "[Django] "
|
||||
EMAIL_SUBJECT_PREFIX: str = ...
|
||||
|
||||
# Whether to append trailing slashes to URLs.
|
||||
APPEND_SLASH = True
|
||||
APPEND_SLASH: bool = ...
|
||||
|
||||
# Whether to prepend the "www." subdomain to URLs that don't have it.
|
||||
PREPEND_WWW = False
|
||||
PREPEND_WWW: bool = ...
|
||||
|
||||
# Override the server-derived value of SCRIPT_NAME
|
||||
FORCE_SCRIPT_NAME = None
|
||||
@@ -237,9 +151,9 @@ FORCE_SCRIPT_NAME = None
|
||||
# re.compile(r'^SiteSucker.*'),
|
||||
# re.compile(r'^sohu-search'),
|
||||
# ]
|
||||
DISALLOWED_USER_AGENTS = []
|
||||
DISALLOWED_USER_AGENTS: List[Pattern] = ...
|
||||
|
||||
ABSOLUTE_URL_OVERRIDES = {}
|
||||
ABSOLUTE_URL_OVERRIDES: Dict[str, Callable[[Model], str]] = ...
|
||||
|
||||
# List of compiled regular expression objects representing URLs that need not
|
||||
# be reported by BrokenLinkEmailsMiddleware. Here are a few examples:
|
||||
@@ -251,54 +165,51 @@ ABSOLUTE_URL_OVERRIDES = {}
|
||||
# re.compile(r'^/phpmyadmin/'),
|
||||
# re.compile(r'\.(cgi|php|pl)$'),
|
||||
# ]
|
||||
IGNORABLE_404_URLS = []
|
||||
IGNORABLE_404_URLS: List[Pattern] = ...
|
||||
|
||||
# A secret key for this particular Django installation. Used in secret-key
|
||||
# hashing algorithms. Set this in your settings, or Django will complain
|
||||
# loudly.
|
||||
SECRET_KEY = ""
|
||||
SECRET_KEY: str = ...
|
||||
|
||||
# Default file storage mechanism that holds media.
|
||||
DEFAULT_FILE_STORAGE = "django.core.files.storage.FileSystemStorage"
|
||||
DEFAULT_FILE_STORAGE: str = ...
|
||||
|
||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||
# Example: "/var/www/example.com/media/"
|
||||
MEDIA_ROOT = ""
|
||||
MEDIA_ROOT: str = ...
|
||||
|
||||
# URL that handles the media served from MEDIA_ROOT.
|
||||
# Examples: "http://example.com/media/", "http://media.example.com/"
|
||||
MEDIA_URL = ""
|
||||
MEDIA_URL: str = ...
|
||||
|
||||
# Absolute path to the directory static files should be collected to.
|
||||
# Example: "/var/www/example.com/static/"
|
||||
STATIC_ROOT = None
|
||||
STATIC_ROOT: Optional[str] = ...
|
||||
|
||||
# URL that handles the static files served from STATIC_ROOT.
|
||||
# Example: "http://example.com/static/", "http://static.example.com/"
|
||||
STATIC_URL = None
|
||||
STATIC_URL: Optional[str] = ...
|
||||
|
||||
# List of upload handler classes to be applied in order.
|
||||
FILE_UPLOAD_HANDLERS = [
|
||||
"django.core.files.uploadhandler.MemoryFileUploadHandler",
|
||||
"django.core.files.uploadhandler.TemporaryFileUploadHandler",
|
||||
]
|
||||
FILE_UPLOAD_HANDLERS: List[str] = ...
|
||||
|
||||
# Maximum size, in bytes, of a request before it will be streamed to the
|
||||
# file system instead of into memory.
|
||||
FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440 # i.e. 2.5 MB
|
||||
FILE_UPLOAD_MAX_MEMORY_SIZE: int = ... # i.e. 2.5 MB
|
||||
|
||||
# Maximum size in bytes of request data (excluding file uploads) that will be
|
||||
# read before a SuspiciousOperation (RequestDataTooBig) is raised.
|
||||
DATA_UPLOAD_MAX_MEMORY_SIZE = 2621440 # i.e. 2.5 MB
|
||||
DATA_UPLOAD_MAX_MEMORY_SIZE: int = ... # i.e. 2.5 MB
|
||||
|
||||
# Maximum number of GET/POST parameters that will be read before a
|
||||
# SuspiciousOperation (TooManyFieldsSent) is raised.
|
||||
DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000
|
||||
DATA_UPLOAD_MAX_NUMBER_FIELDS: int = ...
|
||||
|
||||
# Directory in which upload streamed files will be temporarily saved. A value of
|
||||
# `None` will make Django use the operating system's default temporary directory
|
||||
# (i.e. "/tmp" on *nix systems).
|
||||
FILE_UPLOAD_TEMP_DIR = None
|
||||
FILE_UPLOAD_TEMP_DIR: Optional[str] = ...
|
||||
|
||||
# The numeric mode to set newly-uploaded files to. The value should be a mode
|
||||
# you'd pass directly to os.chmod; see https://docs.python.org/library/os.html#files-and-directories.
|
||||
@@ -313,116 +224,91 @@ FILE_UPLOAD_DIRECTORY_PERMISSIONS = None
|
||||
# The directory where this setting is pointing should contain subdirectories
|
||||
# named as the locales, containing a formats.py file
|
||||
# (i.e. "myproject.locale" for myproject/locale/en/formats.py etc. use)
|
||||
FORMAT_MODULE_PATH = None
|
||||
FORMAT_MODULE_PATH: Optional[str] = ...
|
||||
|
||||
# Default formatting for date objects. See all available format strings here:
|
||||
# https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||
DATE_FORMAT = "N j, Y"
|
||||
DATE_FORMAT: str = ...
|
||||
|
||||
# Default formatting for datetime objects. See all available format strings here:
|
||||
# https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||
DATETIME_FORMAT = "N j, Y, P"
|
||||
DATETIME_FORMAT: str = ...
|
||||
|
||||
# Default formatting for time objects. See all available format strings here:
|
||||
# https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||
TIME_FORMAT = "P"
|
||||
TIME_FORMAT: str = ...
|
||||
|
||||
# Default formatting for date objects when only the year and month are relevant.
|
||||
# See all available format strings here:
|
||||
# https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||
YEAR_MONTH_FORMAT = "F Y"
|
||||
YEAR_MONTH_FORMAT: str = ...
|
||||
|
||||
# Default formatting for date objects when only the month and day are relevant.
|
||||
# See all available format strings here:
|
||||
# https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||
MONTH_DAY_FORMAT = "F j"
|
||||
MONTH_DAY_FORMAT: str = ...
|
||||
|
||||
# Default short formatting for date objects. See all available format strings here:
|
||||
# https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||
SHORT_DATE_FORMAT = "m/d/Y"
|
||||
SHORT_DATE_FORMAT: str = ...
|
||||
|
||||
# Default short formatting for datetime objects.
|
||||
# See all available format strings here:
|
||||
# https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||
SHORT_DATETIME_FORMAT = "m/d/Y P"
|
||||
SHORT_DATETIME_FORMAT: str = ...
|
||||
|
||||
# Default formats to be used when parsing dates from input boxes, in order
|
||||
# See all available format string here:
|
||||
# https://docs.python.org/library/datetime.html#strftime-behavior
|
||||
# * Note that these format strings are different from the ones to display dates
|
||||
DATE_INPUT_FORMATS = [
|
||||
"%Y-%m-%d",
|
||||
"%m/%d/%Y",
|
||||
"%m/%d/%y", # '2006-10-25', '10/25/2006', '10/25/06'
|
||||
"%b %d %Y",
|
||||
"%b %d, %Y", # 'Oct 25 2006', 'Oct 25, 2006'
|
||||
"%d %b %Y",
|
||||
"%d %b, %Y", # '25 Oct 2006', '25 Oct, 2006'
|
||||
"%B %d %Y",
|
||||
"%B %d, %Y", # 'October 25 2006', 'October 25, 2006'
|
||||
"%d %B %Y",
|
||||
"%d %B, %Y", # '25 October 2006', '25 October, 2006'
|
||||
]
|
||||
DATE_INPUT_FORMATS: List[str] = ...
|
||||
|
||||
# Default formats to be used when parsing times from input boxes, in order
|
||||
# See all available format string here:
|
||||
# https://docs.python.org/library/datetime.html#strftime-behavior
|
||||
# * Note that these format strings are different from the ones to display dates
|
||||
TIME_INPUT_FORMATS = ["%H:%M:%S", "%H:%M:%S.%f", "%H:%M"] # '14:30:59' # '14:30:59.000200' # '14:30'
|
||||
TIME_INPUT_FORMATS: List[str] = ... # '14:30:59' # '14:30:59.000200' # '14:30'
|
||||
|
||||
# Default formats to be used when parsing dates and times from input boxes,
|
||||
# in order
|
||||
# See all available format string here:
|
||||
# https://docs.python.org/library/datetime.html#strftime-behavior
|
||||
# * Note that these format strings are different from the ones to display dates
|
||||
DATETIME_INPUT_FORMATS = [
|
||||
"%Y-%m-%d %H:%M:%S", # '2006-10-25 14:30:59'
|
||||
"%Y-%m-%d %H:%M:%S.%f", # '2006-10-25 14:30:59.000200'
|
||||
"%Y-%m-%d %H:%M", # '2006-10-25 14:30'
|
||||
"%Y-%m-%d", # '2006-10-25'
|
||||
"%m/%d/%Y %H:%M:%S", # '10/25/2006 14:30:59'
|
||||
"%m/%d/%Y %H:%M:%S.%f", # '10/25/2006 14:30:59.000200'
|
||||
"%m/%d/%Y %H:%M", # '10/25/2006 14:30'
|
||||
"%m/%d/%Y", # '10/25/2006'
|
||||
"%m/%d/%y %H:%M:%S", # '10/25/06 14:30:59'
|
||||
"%m/%d/%y %H:%M:%S.%f", # '10/25/06 14:30:59.000200'
|
||||
"%m/%d/%y %H:%M", # '10/25/06 14:30'
|
||||
"%m/%d/%y", # '10/25/06'
|
||||
]
|
||||
DATETIME_INPUT_FORMATS: List[str] = ...
|
||||
|
||||
# First day of week, to be used on calendars
|
||||
# 0 means Sunday, 1 means Monday...
|
||||
FIRST_DAY_OF_WEEK = 0
|
||||
FIRST_DAY_OF_WEEK: int = ...
|
||||
|
||||
# Decimal separator symbol
|
||||
DECIMAL_SEPARATOR = "."
|
||||
DECIMAL_SEPARATOR: str = ...
|
||||
|
||||
# Boolean that sets whether to add thousand separator when formatting numbers
|
||||
USE_THOUSAND_SEPARATOR = False
|
||||
USE_THOUSAND_SEPARATOR: bool = ...
|
||||
|
||||
# Number of digits that will be together, when splitting them by
|
||||
# THOUSAND_SEPARATOR. 0 means no grouping, 3 means splitting by thousands...
|
||||
NUMBER_GROUPING = 0
|
||||
NUMBER_GROUPING: int = ...
|
||||
|
||||
# Thousand separator symbol
|
||||
THOUSAND_SEPARATOR = ","
|
||||
THOUSAND_SEPARATOR: str = ...
|
||||
|
||||
# The tablespaces to use for each model when not specified otherwise.
|
||||
DEFAULT_TABLESPACE = ""
|
||||
DEFAULT_INDEX_TABLESPACE = ""
|
||||
DEFAULT_TABLESPACE: str = ...
|
||||
DEFAULT_INDEX_TABLESPACE: str = ...
|
||||
|
||||
# Default X-Frame-Options header value
|
||||
X_FRAME_OPTIONS = "SAMEORIGIN"
|
||||
X_FRAME_OPTIONS: str = ...
|
||||
|
||||
USE_X_FORWARDED_HOST = False
|
||||
USE_X_FORWARDED_PORT = False
|
||||
USE_X_FORWARDED_HOST: bool = ...
|
||||
USE_X_FORWARDED_PORT: bool = ...
|
||||
|
||||
# The Python dotted path to the WSGI application that Django's internal server
|
||||
# (runserver) will use. If `None`, the return value of
|
||||
# 'django.core.wsgi.get_wsgi_application' is used, thus preserving the same
|
||||
# behavior as previous versions of Django. Otherwise this should point to an
|
||||
# actual WSGI application object.
|
||||
WSGI_APPLICATION = None
|
||||
WSGI_APPLICATION: Optional[str] = ...
|
||||
|
||||
# If your Django app is behind a proxy that sets a header to specify secure
|
||||
# connections, AND that proxy ensures that user-submitted headers with the
|
||||
@@ -431,7 +317,7 @@ WSGI_APPLICATION = None
|
||||
# that header/value, request.is_secure() will return True.
|
||||
# WARNING! Only set this if you fully understand what you're doing. Otherwise,
|
||||
# you may be opening yourself up to a security risk.
|
||||
SECURE_PROXY_SSL_HEADER = None
|
||||
SECURE_PROXY_SSL_HEADER: Optional[Tuple[str, str]] = ...
|
||||
|
||||
##############
|
||||
# MIDDLEWARE #
|
||||
@@ -440,7 +326,7 @@ SECURE_PROXY_SSL_HEADER = None
|
||||
# List of middleware to use. Order is important; in the request phase, these
|
||||
# middleware will be applied in the order given, and in the response
|
||||
# phase the middleware will be applied in reverse order.
|
||||
MIDDLEWARE = []
|
||||
MIDDLEWARE: List[str] = ...
|
||||
|
||||
############
|
||||
# SESSIONS #
|
||||
@@ -453,7 +339,7 @@ SESSION_COOKIE_NAME = "sessionid"
|
||||
# Age of cookie, in seconds (default: 2 weeks).
|
||||
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
|
||||
# A string like "example.com", or None for standard domain cookie.
|
||||
SESSION_COOKIE_DOMAIN = None
|
||||
SESSION_COOKIE_DOMAIN: Optional[str] = ...
|
||||
# Whether the session cookie should be secure (https:// only).
|
||||
SESSION_COOKIE_SECURE = False
|
||||
# The path of the session cookie.
|
||||
@@ -471,7 +357,7 @@ SESSION_EXPIRE_AT_BROWSER_CLOSE = False
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.db"
|
||||
# Directory to store session files if using the file session module. If None,
|
||||
# the backend will use a sensible default.
|
||||
SESSION_FILE_PATH = None
|
||||
SESSION_FILE_PATH: Optional[str] = ...
|
||||
# class to serialize session data
|
||||
SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
|
||||
|
||||
@@ -480,7 +366,7 @@ SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
|
||||
#########
|
||||
|
||||
# The cache backends to use.
|
||||
CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}
|
||||
CACHES: Dict[str, Dict[str, Any]] = ...
|
||||
CACHE_MIDDLEWARE_KEY_PREFIX = ""
|
||||
CACHE_MIDDLEWARE_SECONDS = 600
|
||||
CACHE_MIDDLEWARE_ALIAS = "default"
|
||||
@@ -489,15 +375,15 @@ CACHE_MIDDLEWARE_ALIAS = "default"
|
||||
# AUTHENTICATION #
|
||||
##################
|
||||
|
||||
AUTH_USER_MODEL = "auth.User"
|
||||
AUTH_USER_MODEL: str = ...
|
||||
|
||||
AUTHENTICATION_BACKENDS = ["django.contrib.auth.backends.ModelBackend"]
|
||||
AUTHENTICATION_BACKENDS: List[str] = ...
|
||||
|
||||
LOGIN_URL = "/accounts/login/"
|
||||
|
||||
LOGIN_REDIRECT_URL = "/accounts/profile/"
|
||||
LOGIN_REDIRECT_URL: str = ...
|
||||
|
||||
LOGOUT_REDIRECT_URL = None
|
||||
LOGOUT_REDIRECT_URL: Optional[str] = ...
|
||||
|
||||
# The number of days a password reset link is valid for
|
||||
PASSWORD_RESET_TIMEOUT_DAYS = 3
|
||||
@@ -505,14 +391,9 @@ PASSWORD_RESET_TIMEOUT_DAYS = 3
|
||||
# the first hasher in this list is the preferred algorithm. any
|
||||
# password using different algorithms will be converted automatically
|
||||
# upon login
|
||||
PASSWORD_HASHERS = [
|
||||
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
|
||||
"django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
|
||||
"django.contrib.auth.hashers.Argon2PasswordHasher",
|
||||
"django.contrib.auth.hashers.BCryptSHA256PasswordHasher",
|
||||
]
|
||||
PASSWORD_HASHERS: List[str] = ...
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = []
|
||||
AUTH_PASSWORD_VALIDATORS: List[Dict[str, str]] = ...
|
||||
|
||||
###########
|
||||
# SIGNING #
|
||||
@@ -537,7 +418,7 @@ CSRF_COOKIE_SECURE = False
|
||||
CSRF_COOKIE_HTTPONLY = False
|
||||
CSRF_COOKIE_SAMESITE = "Lax"
|
||||
CSRF_HEADER_NAME = "HTTP_X_CSRFTOKEN"
|
||||
CSRF_TRUSTED_ORIGINS = []
|
||||
CSRF_TRUSTED_ORIGINS: List[str] = ...
|
||||
CSRF_USE_SESSIONS = False
|
||||
|
||||
############
|
||||
@@ -558,7 +439,7 @@ MESSAGE_STORAGE = "django.contrib.messages.storage.fallback.FallbackStorage"
|
||||
LOGGING_CONFIG = "logging.config.dictConfig"
|
||||
|
||||
# Custom logging configuration.
|
||||
LOGGING = {}
|
||||
LOGGING: Dict[str, Any] = ...
|
||||
|
||||
# Default exception reporter filter class used in case none has been
|
||||
# specifically assigned to the HttpRequest instance.
|
||||
@@ -573,39 +454,35 @@ TEST_RUNNER = "django.test.runner.DiscoverRunner"
|
||||
|
||||
# Apps that don't need to be serialized at test database creation time
|
||||
# (only apps with migrations are to start with)
|
||||
TEST_NON_SERIALIZED_APPS = []
|
||||
TEST_NON_SERIALIZED_APPS: List[str] = ...
|
||||
|
||||
############
|
||||
# FIXTURES #
|
||||
############
|
||||
|
||||
# The list of directories to search for fixtures
|
||||
FIXTURE_DIRS = []
|
||||
FIXTURE_DIRS: List[str] = ...
|
||||
|
||||
###############
|
||||
# STATICFILES #
|
||||
###############
|
||||
|
||||
# A list of locations of additional static files
|
||||
STATICFILES_DIRS = []
|
||||
STATICFILES_DIRS: List[str] = ...
|
||||
|
||||
# The default file storage backend used during the build process
|
||||
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage"
|
||||
STATICFILES_STORAGE: str = ...
|
||||
|
||||
# List of finder classes that know how to find static files in
|
||||
# various locations.
|
||||
STATICFILES_FINDERS = [
|
||||
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
|
||||
]
|
||||
STATICFILES_FINDERS: List[str] = ...
|
||||
|
||||
##############
|
||||
# MIGRATIONS #
|
||||
##############
|
||||
|
||||
# Migration module overrides for apps, by app label.
|
||||
MIGRATION_MODULES = {}
|
||||
MIGRATION_MODULES: Dict[str, str] = ...
|
||||
|
||||
#################
|
||||
# SYSTEM CHECKS #
|
||||
@@ -615,7 +492,7 @@ MIGRATION_MODULES = {}
|
||||
# issues like warnings, infos or debugs will not generate a message. Silencing
|
||||
# serious issues like errors and criticals does not result in hiding the
|
||||
# message, but Django will not stop you from e.g. running server.
|
||||
SILENCED_SYSTEM_CHECKS = []
|
||||
SILENCED_SYSTEM_CHECKS: List[str] = ...
|
||||
|
||||
#######################
|
||||
# SECURITY MIDDLEWARE #
|
||||
@@ -625,6 +502,6 @@ SECURE_CONTENT_TYPE_NOSNIFF = False
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = False
|
||||
SECURE_HSTS_PRELOAD = False
|
||||
SECURE_HSTS_SECONDS = 0
|
||||
SECURE_REDIRECT_EXEMPT = []
|
||||
SECURE_REDIRECT_EXEMPT: List[str] = ...
|
||||
SECURE_SSL_HOST = None
|
||||
SECURE_SSL_REDIRECT = False
|
||||
|
||||
@@ -58,7 +58,7 @@ class PermissionsMixin(models.Model):
|
||||
def has_perms(self, perm_list: Union[List[str], Set[str], Tuple[str]], obj: None = ...) -> bool: ...
|
||||
def has_module_perms(self, app_label: str) -> bool: ...
|
||||
|
||||
class AbstractUser(AbstractBaseUser, PermissionsMixin):
|
||||
class AbstractUser(AbstractBaseUser, PermissionsMixin): # type: ignore
|
||||
is_superuser: bool
|
||||
username_validator: Any = ...
|
||||
username: str = ...
|
||||
|
||||
@@ -2,7 +2,7 @@ from typing import Any, Callable, Dict, List, Optional, Type, Union, Sequence, P
|
||||
|
||||
from django.db.models import Manager, QuerySet
|
||||
from django.db.models.base import Model
|
||||
from django.http.response import HttpResponse, HttpResponseRedirect
|
||||
from django.http.response import HttpResponse as HttpResponse, HttpResponseRedirect as HttpResponseRedirect
|
||||
|
||||
from django.http import HttpRequest
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ class override(ContextDecorator):
|
||||
def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None: ...
|
||||
|
||||
def get_language() -> Optional[str]: ...
|
||||
def get_language_from_path(path: str) -> Optional[str]: ...
|
||||
def get_language_bidi() -> bool: ...
|
||||
def check_for_language(lang_code: Optional[str]) -> bool: ...
|
||||
def to_language(locale: str) -> str: ...
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import os
|
||||
from typing import Callable, Optional, cast, Dict
|
||||
from typing import Callable, Dict, Optional, cast
|
||||
|
||||
from mypy.checker import TypeChecker
|
||||
from mypy.nodes import TypeInfo
|
||||
from mypy.options import Options
|
||||
from mypy.plugin import Plugin, FunctionContext, ClassDefContext, MethodContext
|
||||
from mypy.types import Type, Instance
|
||||
from mypy.plugin import ClassDefContext, FunctionContext, MethodContext, Plugin
|
||||
from mypy.types import Instance, Type
|
||||
|
||||
from mypy_django_plugin import helpers, monkeypatch
|
||||
from mypy_django_plugin.plugins.fields import determine_type_of_array_field
|
||||
from mypy_django_plugin.plugins.migrations import determine_model_cls_from_string_for_migrations
|
||||
from mypy_django_plugin.plugins.models import process_model_class
|
||||
from mypy_django_plugin.plugins.related_fields import extract_to_parameter_as_get_ret_type_for_related_field, reparametrize_with
|
||||
from mypy_django_plugin.plugins.settings import DjangoConfSettingsInitializerHook
|
||||
from mypy_django_plugin.plugins.settings import AddSettingValuesToDjangoConfObject
|
||||
|
||||
|
||||
def transform_model_class(ctx: ClassDefContext) -> None:
|
||||
@@ -55,19 +55,21 @@ def determine_proper_manager_type(ctx: FunctionContext) -> Type:
|
||||
|
||||
|
||||
class DjangoPlugin(Plugin):
|
||||
def __init__(self,
|
||||
options: Options) -> None:
|
||||
def __init__(self, options: Options) -> None:
|
||||
super().__init__(options)
|
||||
|
||||
monkeypatch.restore_original_load_graph()
|
||||
monkeypatch.restore_original_dependencies_handling()
|
||||
|
||||
settings_modules = ['django.conf.global_settings']
|
||||
self.django_settings = os.environ.get('DJANGO_SETTINGS_MODULE')
|
||||
if self.django_settings:
|
||||
monkeypatch.load_graph_to_add_settings_file_as_a_source_seed(self.django_settings)
|
||||
monkeypatch.inject_dependencies(self.django_settings)
|
||||
else:
|
||||
monkeypatch.restore_original_load_graph()
|
||||
monkeypatch.restore_original_dependencies_handling()
|
||||
settings_modules.append(self.django_settings)
|
||||
|
||||
def get_current_model_bases(self) -> Dict[str, int]:
|
||||
monkeypatch.add_modules_as_a_source_seed_files(settings_modules)
|
||||
monkeypatch.inject_modules_as_dependencies_for_django_conf_settings(settings_modules)
|
||||
|
||||
def _get_current_model_bases(self) -> Dict[str, int]:
|
||||
model_sym = self.lookup_fully_qualified(helpers.MODEL_CLASS_FULLNAME)
|
||||
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
||||
if 'django' not in model_sym.node.metadata:
|
||||
@@ -78,7 +80,7 @@ class DjangoPlugin(Plugin):
|
||||
else:
|
||||
return {}
|
||||
|
||||
def get_current_manager_bases(self) -> Dict[str, int]:
|
||||
def _get_current_manager_bases(self) -> Dict[str, int]:
|
||||
manager_sym = self.lookup_fully_qualified(helpers.MANAGER_CLASS_FULLNAME)
|
||||
if manager_sym is not None and isinstance(manager_sym.node, TypeInfo):
|
||||
if 'django' not in manager_sym.node.metadata:
|
||||
@@ -99,7 +101,7 @@ class DjangoPlugin(Plugin):
|
||||
if fullname == 'django.contrib.postgres.fields.array.ArrayField':
|
||||
return determine_type_of_array_field
|
||||
|
||||
manager_bases = self.get_current_manager_bases()
|
||||
manager_bases = self._get_current_manager_bases()
|
||||
if fullname in manager_bases:
|
||||
return determine_proper_manager_type
|
||||
|
||||
@@ -112,13 +114,16 @@ class DjangoPlugin(Plugin):
|
||||
|
||||
def get_base_class_hook(self, fullname: str
|
||||
) -> Optional[Callable[[ClassDefContext], None]]:
|
||||
if fullname in self.get_current_model_bases():
|
||||
if fullname in self._get_current_model_bases():
|
||||
return transform_model_class
|
||||
|
||||
if fullname == helpers.DUMMY_SETTINGS_BASE_CLASS:
|
||||
return DjangoConfSettingsInitializerHook(settings_module=self.django_settings)
|
||||
settings_modules = ['django.conf.global_settings']
|
||||
if self.django_settings:
|
||||
settings_modules.append(self.django_settings)
|
||||
return AddSettingValuesToDjangoConfObject(settings_modules)
|
||||
|
||||
if fullname in self.get_current_manager_bases():
|
||||
if fullname in self._get_current_manager_bases():
|
||||
return transform_manager_class
|
||||
|
||||
return None
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from .dependencies import (load_graph_to_add_settings_file_as_a_source_seed,
|
||||
inject_dependencies,
|
||||
from .dependencies import (add_modules_as_a_source_seed_files,
|
||||
inject_modules_as_dependencies_for_django_conf_settings,
|
||||
restore_original_load_graph,
|
||||
restore_original_dependencies_handling,
|
||||
process_settings_before_dependants)
|
||||
restore_original_dependencies_handling)
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
from typing import List, Optional, AbstractSet, MutableSet, Set
|
||||
from typing import List, Optional
|
||||
|
||||
from mypy.build import BuildManager, Graph, State, PRI_ALL
|
||||
from mypy import build
|
||||
from mypy.build import BuildManager, Graph, State
|
||||
from mypy.modulefinder import BuildSource
|
||||
|
||||
old_load_graph = build.load_graph
|
||||
OldState = build.State
|
||||
|
||||
|
||||
def is_module_present_in_sources(module_name: str, sources: List[BuildSource]):
|
||||
return any([source.module == module_name for source in sources])
|
||||
|
||||
|
||||
from mypy import build
|
||||
|
||||
old_load_graph = build.load_graph
|
||||
OldState = build.State
|
||||
old_sorted_components = build.sorted_components
|
||||
|
||||
|
||||
def load_graph_to_add_settings_file_as_a_source_seed(settings_module: str):
|
||||
def add_modules_as_a_source_seed_files(modules: List[str]) -> None:
|
||||
def patched_load_graph(sources: List[BuildSource], manager: BuildManager,
|
||||
old_graph: Optional[Graph] = None,
|
||||
new_modules: Optional[List[State]] = None):
|
||||
if not is_module_present_in_sources(settings_module, sources):
|
||||
sources.append(BuildSource(None, settings_module, None))
|
||||
# add global settings
|
||||
for module_name in modules:
|
||||
if not is_module_present_in_sources(module_name, sources):
|
||||
sources.append(BuildSource(None, module_name, None))
|
||||
|
||||
return old_load_graph(sources=sources, manager=manager,
|
||||
old_graph=old_graph,
|
||||
@@ -35,14 +34,14 @@ def restore_original_load_graph():
|
||||
build.load_graph = old_load_graph
|
||||
|
||||
|
||||
def inject_dependencies(settings_module: str):
|
||||
def inject_modules_as_dependencies_for_django_conf_settings(modules: List[str]) -> None:
|
||||
from mypy import build
|
||||
|
||||
class PatchedState(build.State):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.id == 'django.conf':
|
||||
self.dependencies.append(settings_module)
|
||||
self.dependencies.extend(modules)
|
||||
|
||||
build.State = PatchedState
|
||||
|
||||
@@ -51,40 +50,3 @@ def restore_original_dependencies_handling():
|
||||
from mypy import build
|
||||
|
||||
build.State = OldState
|
||||
|
||||
|
||||
def _extract_dependencies(graph: Graph, state_id: str, visited_modules: Set[str]) -> Set[str]:
|
||||
visited_modules.add(state_id)
|
||||
dependencies = set(graph[state_id].dependencies)
|
||||
for new_dep_id in dependencies.copy():
|
||||
if new_dep_id not in visited_modules:
|
||||
dependencies.update(_extract_dependencies(graph, new_dep_id, visited_modules))
|
||||
return dependencies
|
||||
|
||||
|
||||
def extract_module_dependencies(graph: Graph, state_id: str) -> Set[str]:
|
||||
visited_modules = set()
|
||||
return _extract_dependencies(graph, state_id, visited_modules=visited_modules)
|
||||
|
||||
|
||||
def process_settings_before_dependants(settings_module: str):
|
||||
def patched_sorted_components(graph: Graph,
|
||||
vertices: Optional[AbstractSet[str]] = None,
|
||||
pri_max: int = PRI_ALL) -> List[AbstractSet[str]]:
|
||||
sccs = old_sorted_components(graph,
|
||||
vertices=vertices,
|
||||
pri_max=pri_max)
|
||||
for i, scc in enumerate(sccs.copy()):
|
||||
if 'django.conf' in scc:
|
||||
django_conf_deps = set(extract_module_dependencies(graph, 'django.conf')).union({'django.conf'})
|
||||
old_scc_modified = scc.difference(django_conf_deps)
|
||||
new_scc = scc.difference(old_scc_modified)
|
||||
if not old_scc_modified:
|
||||
# already processed
|
||||
break
|
||||
sccs[i] = frozenset(old_scc_modified)
|
||||
sccs.insert(i, frozenset(new_scc))
|
||||
break
|
||||
return sccs
|
||||
|
||||
build.sorted_components = patched_sorted_components
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import dataclasses
|
||||
from abc import abstractmethod, ABCMeta
|
||||
from typing import cast, Iterator, Tuple, Optional, Dict
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from typing import Dict, Iterator, Optional, Tuple, cast
|
||||
|
||||
from mypy.nodes import ClassDef, AssignmentStmt, CallExpr, MemberExpr, StrExpr, NameExpr, MDEF, TypeInfo, Var, SymbolTableNode, \
|
||||
Lvalue, Expression, MypyFile, Context
|
||||
import dataclasses
|
||||
from mypy.nodes import AssignmentStmt, CallExpr, ClassDef, Context, Expression, Lvalue, MDEF, MemberExpr, MypyFile, NameExpr, \
|
||||
StrExpr, SymbolTableNode, TypeInfo, Var
|
||||
from mypy.plugin import ClassDefContext
|
||||
from mypy.semanal import SemanticAnalyzerPass2
|
||||
from mypy.types import Instance
|
||||
@@ -45,6 +45,7 @@ class ModelClassInitializer(metaclass=ABCMeta):
|
||||
var._fullname = self.model_classdef.info.fullname() + '.' + name
|
||||
var.is_inferred = True
|
||||
var.is_initialized_in_class = True
|
||||
var.is_classvar = True
|
||||
self.model_classdef.info.names[name] = SymbolTableNode(MDEF, var)
|
||||
|
||||
@abstractmethod
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from typing import cast, List, Optional
|
||||
from typing import List, Optional, cast
|
||||
|
||||
from mypy.nodes import Var, Context, SymbolNode, SymbolTableNode
|
||||
from mypy.nodes import ClassDef, Context, MypyFile, SymbolNode, SymbolTableNode, Var
|
||||
from mypy.plugin import ClassDefContext
|
||||
from mypy.semanal import SemanticAnalyzerPass2
|
||||
from mypy.types import Instance, UnionType, NoneTyp, Type
|
||||
from mypy.types import Instance, NoneTyp, Type, UnionType
|
||||
|
||||
|
||||
def get_error_context(node: SymbolNode) -> Context:
|
||||
@@ -36,39 +36,22 @@ def make_sym_copy_of_setting(sym: SymbolTableNode) -> Optional[SymbolTableNode]:
|
||||
return None
|
||||
|
||||
|
||||
def add_settings_to_django_conf_object(ctx: ClassDefContext,
|
||||
settings_module: str) -> None:
|
||||
api = cast(SemanticAnalyzerPass2, ctx.api)
|
||||
if settings_module not in api.modules:
|
||||
return None
|
||||
|
||||
settings_file = api.modules[settings_module]
|
||||
for name, sym in settings_file.names.items():
|
||||
def load_settings_from_module(settings_classdef: ClassDef, module: MypyFile) -> None:
|
||||
for name, sym in module.names.items():
|
||||
if name.isupper() and isinstance(sym.node, Var):
|
||||
if sym.type is not None:
|
||||
copied = make_sym_copy_of_setting(sym)
|
||||
if copied is None:
|
||||
continue
|
||||
ctx.cls.info.names[name] = copied
|
||||
# else:
|
||||
# TODO: figure out suggestion to add type annotation
|
||||
# context = Context()
|
||||
# module, node_name = sym.node.fullname().rsplit('.', 1)
|
||||
# module_file = api.modules.get(module)
|
||||
# if module_file is None:
|
||||
# return None
|
||||
# context.set_line(sym.node)
|
||||
# api.msg.report(f"Need type annotation for '{sym.node.name()}'", context,
|
||||
# severity='error', file=module_file.path)
|
||||
ctx.cls.info.fallback_to_any = True
|
||||
settings_classdef.info.names[name] = copied
|
||||
|
||||
|
||||
class DjangoConfSettingsInitializerHook(object):
|
||||
def __init__(self, settings_module: Optional[str]):
|
||||
self.settings_module = settings_module
|
||||
class AddSettingValuesToDjangoConfObject:
|
||||
def __init__(self, settings_modules: List[str]):
|
||||
self.settings_modules = settings_modules
|
||||
|
||||
def __call__(self, ctx: ClassDefContext) -> None:
|
||||
if not self.settings_module:
|
||||
return
|
||||
|
||||
add_settings_to_django_conf_object(ctx, self.settings_module)
|
||||
api = cast(SemanticAnalyzerPass2, ctx.api)
|
||||
for module_name in self.settings_modules:
|
||||
module = api.modules[module_name]
|
||||
load_settings_from_module(ctx.cls, module=module)
|
||||
|
||||
@@ -32,30 +32,13 @@ from pathlib import Path
|
||||
|
||||
ROOT_DIR = Path(__file__)
|
||||
|
||||
[CASE test_circular_dependency_in_settings]
|
||||
[CASE global_settings_are_always_loaded]
|
||||
from django.conf import settings
|
||||
class Class:
|
||||
pass
|
||||
reveal_type(settings.REGISTRY) # E: Revealed type is 'Union[main.Class, None]'
|
||||
reveal_type(settings.LIST) # E: Revealed type is 'Any'
|
||||
reveal_type(settings.BASE_LIST) # E: Revealed type is 'Any'
|
||||
|
||||
reveal_type(settings.AUTH_USER_MODEL) # E: Revealed type is 'builtins.str'
|
||||
reveal_type(settings.AUTHENTICATION_BACKENDS) # E: Revealed type is 'builtins.list[builtins.str]'
|
||||
[out]
|
||||
|
||||
[env DJANGO_SETTINGS_MODULE=mysettings]
|
||||
[file mysettings.py]
|
||||
from typing import Optional
|
||||
from base import *
|
||||
|
||||
LIST = ['1', '2']
|
||||
|
||||
[file base.py]
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
if TYPE_CHECKING:
|
||||
from main import Class
|
||||
|
||||
REGISTRY: Optional['Class'] = None
|
||||
BASE_LIST = ['3', '4']
|
||||
|
||||
[CASE test_circular_dependency_in_settings_works_if_settings_have_annotations]
|
||||
from django.conf import settings
|
||||
class Class:
|
||||
@@ -76,10 +59,12 @@ MYSETTING = 1122
|
||||
REGISTRY: Optional['Class'] = None
|
||||
LIST: List[str] = ['1', '2']
|
||||
|
||||
[CASE allow_calls_to_nonexistent_members_for_now]
|
||||
[CASE fail_if_there_is_no_setting]
|
||||
from django.conf import settings
|
||||
reveal_type(settings.NOT_EXISTING) # E: Revealed type is 'Any'
|
||||
reveal_type(settings.NOT_EXISTING)
|
||||
|
||||
[env DJANGO_SETTINGS_MODULE=mysettings]
|
||||
[file mysettings.py]
|
||||
[out]
|
||||
main:2: error: Revealed type is 'Any'
|
||||
main:2: error: "LazySettings" has no attribute "NOT_EXISTING"
|
||||
Reference in New Issue
Block a user